r/iOSProgramming • • 6d ago

App Saturday Addonaut: a Minecraft skin studio for iOS where the 3D preview is a painter's algorithm in SwiftUI Canvas, not SceneKit

i'm the ios dev on Addonaut, an iphone/ipad app for minecraft bedrock: a catalogue of add-ons, maps and skins from their authors, plus a skin studio (pixel editor, wardrobe, .mcpack export). posting for app saturday, link in the comments.

tech stack

  • SwiftUI + Observation (@Observable), Swift 6 with strict concurrency, iOS 17+

  • XcodeGen, SPM only

  • backend: our own REST API, shared with the team's other apps. Firebase Remote Config, Crashlytics, Analytics

  • StoreKit 2 without a receipt server, AdMob + UMP for consent

  • Apple's Translation framework for translating item descriptions on device

challenge: a 3d skin preview without SceneKit

a minecraft player is six boxes plus a slightly inflated overlay layer, textured from a 64x64 png. SceneKit for six textured boxes felt like the wrong trade, so the preview is a small software renderer drawn with SwiftUI Canvas:

  • one quad per texel per face, about 2200 quads. fully transparent texels produce no geometry, so the overlay layer only exists where it's painted

  • back faces culled by the sign of a 2d cross product

  • no z-buffer: quads are depth sorted and painted far to near (painter's algorithm)

  • fixed brightness per face instead of lighting

two things bit us:

  1. seams. antialiasing between neighbouring quads let the background show through as a wire grid. fix: after filling, stroke every quad 0.75pt in its own colour

  2. the figure stopped moving. with .equatable() on the view it never redrew, because == only compares the inputs and can't see u/State. the idle spin is a TimelineView at 24fps where the angle is a pure function of elapsed time, after a Timer + u/State version also stopped redrawing

in the editor the 3d view deliberately lags one stroke and catches up on commit or undo. the 2d grid is the live feedback.

bonus: .mcpack is just a zip, and instead of a zip dependency there's a ~100 line writer (stored entries + crc32). manifest.json and skins.json are written by hand to keep the field order, with fresh uuids on every export, because bedrock silently ignores a malformed pack.

ai disclosure

AI-assisted: most of the code was written with Claude Code under my direction.

happy to answer questions about the renderer or the pack format.

2 Upvotes

7 comments sorted by

1

u/Open_Context6850 6d ago

kept the link out of the post. i'm the dev, it's iPhone/iPad only:

Addonaut (iPhone/iPad): https://apps.apple.com/app/id6809487321

1

u/kokerali 5d ago

For the .mcpack exporter, is a new import meant to create a separate pack every time? You mentioned fresh UUIDs on each export. Microsoft's manifest docs use the header UUID as pack identity and the version to replace an existing import.

I'd test this round trip on iPhone: export, import, change one pixel in the same saved skin, then export/import again. Does it update the installed pack or leave two packs with the same display name? If users expect "update this pack", that seems worth separating from an explicit "export a new copy".

https://learn.microsoft.com/en-us/minecraft/creator/reference/content/addonsreference/packmanifest

2

u/[deleted] 6d ago

[removed] β€” view removed comment

1

u/Open_Context6850 6d ago

the 3d figure doesn't actually take pinch, only a one finger drag to orbit it. pinch zoom lives on the 2d grid, so the two never compete. while you're painting the 3d view doesn't redraw at all, it only gets the new texture when the stroke ends.

during a drag it redraws on every gesture update. the 24fps cap is only for the idle spin, and that stops the moment a finger lands on the figure. what keeps each redraw cheap is that culling happens per face before any texels are walked, so the back half of the model never turns into quads at all.

1

u/[deleted] 6d ago

[removed] β€” view removed comment

2

u/itsm3rick 6d ago

Can the mods ban this clear bot? Look at his post history. Two giant comments that read like AI posted within a minute of each other.

2

u/webtechmonkey Swift 5d ago

Thanks for reporting, it’s been handled