Shipping an iOS game at 8.6 MB with zero third-party SDKs
A few days ago I shipped an iPhone arcade game. The final download is 8.6 MB, and there is not a single third-party SDK in it. No ad SDK, no analytics SDK, no crash reporter, no sign-up, no advertising identifier. The dependency list is the platform itself: Swift, SwiftUI for the shell, SpriteKit for the game, CoreMotion for tilt, AVAudioEngine for sound, GameKit for Game Center player names, and StoreKit 2 for one in-app purchase.
One disclosure up front: this was built solo but with heavy use of an AI coding assistant. The architecture and the debugging calls were mine; a lot of the typing wasn't.
"Zero SDKs" started as a build constraint, partly on principle and partly out of curiosity about whether it was still practical in 2026. It turned out to be more practical than I expected, and it shaped the two most interesting engineering decisions in the project. This post is about those two decisions, about the one piece of server infrastructure I did stand up, and about what the constraint actually cost me.
Where the size went: audio is synthesized, not shipped
The game is five arcade genres inside one endless run. The first level is a cloud jumper with a mechanic I care about more than anything else in the project: every climb picks one of 60 classic public-domain melodies, and each bounce plays the next note. The line you take through the clouds becomes the rhythm. Six instruments (piano, guitar, music box, bell, marimba, organ) are matched to the tune.
The naive way to build that is 60 melodies times 6 instruments worth of audio files, plus every sound effect in the game. That alone would be an order of magnitude bigger than my entire shipped binary.
Instead, nothing musical ships as an asset. Every sound is generated at runtime in an AVAudioEngine player pool, and the melody system is a tone cache keyed on (midi note, instrument). When the player bounces, the game asks the cache for the next note of the current tune in the current instrument, synthesizes it if it has never been played before, and plays it. Sixty melodies are just data: note sequences, a few bytes each. Six instruments are synthesis parameters, not sample libraries.
That decision is most of why the number is 8.6 MB and not 200.
The Simulator cannot play a tilt game, so I built a rig that could
Four of the five levels are steered by tilt. That created a testing problem I have not seen written about much: you cannot playtest a tilt game in the iOS Simulator at all. CoreMotion is simply not there. isDeviceMotionAvailable returns false, my motion input code bails out exactly like it should, and both control axes sit at dead center forever.
The fix came in two layers, both behind a dev flag that is compiled out of the shipping build.
First, a keyboard driver. My input system has one seam where tilt becomes a pair of -1 to 1 axes. I wrote a driver that turns held keys into the same axes, with a ramp so a held key deflects like a turning wrist instead of stepping instantly. Nothing downstream can tell a key from a wrist, so the whole game became playable on a Mac keyboard.
Second, bots. Once the input seam accepted synthetic axes, I built an autopilot: five bots, one per level, sharing a single skill dial from 0 to 1. That turned difficulty from an opinion into a measurement. I logged deaths per minute per level, then calibrated the dial against my own play so the numbers meant something outside the rig.
The validation step mattered most. At skill 1.0, the bot took roughly 550 spike encounters across a long unattended run with zero deaths. That is what made the lower-skill numbers trustworthy: if the bot dies at skill 0.5, it is the level's difficulty being measured, not a bad driver.
The rig paid for itself before launch. It surfaced one hazard that was fatal 27% of the time it was encountered, far outside the band everything else sat in. No human ever met it. The bot found it during a pre-launch measurement run, and I would otherwise have shipped it. It got redesigned instead. The rig also lets me record clean gameplay footage on demand, which is its own quiet win for a solo developer.
The one thing I did stand up
There is a server, and I want to be precise about it rather than let "zero SDKs" imply "no backend".
It is a small Node service holding an optional world leaderboard, a remote config document, and the privacy and support pages App Review requires you to host. The game plays completely without it: every level, every round, offline, forever. The leaderboard is the only thing you lose.
What it stores, per submitted score: a random device UUID, whatever nickname you typed, the score and lap, and the country the score came from. The country is worked out server-side from the request IP, which is then discarded. What gets written is the country plus a one-way keyed hash of the address, so I can tell distinct players apart without keeping the address itself.
That is the honest exception to "no analytics." I have no analytics SDK and no advertising identifier, but I do know roughly where in the world people are playing, because I chose to. It is first-party, it is disclosed in the privacy policy, and nothing is shared with anyone.
What zero SDKs actually costs
Honesty section. The constraint is not free.
No analytics SDK means no funnels, no retention curves, no event tracking. I get App Store Connect's aggregate, opt-in numbers, the coarse country breakdown above, and whatever players tell me directly. For a solo arcade game I decided that was enough, and there is a real sense in which not having a dashboard to stare at is a feature. I would think harder about this tradeoff for a product with a subscription funnel.
No crash reporter means I rely on the opt-in crash logs that flow into Xcode's Organizer. Coverage is partial. So far the answer has been to make the game small enough and simple enough that crashes are rare, and to keep shipping builds through TestFlight where reports are richer.
No receipt server, though, is not a cost at all anymore. The game has one $3.99 unlock, StoreKit 2 verifies transactions on device, and I never stood up any purchase infrastructure. If you have been away from iOS for a few years, this part got dramatically better.
The quieter benefits: App Review had less to question, there is no consent banner, the game works fully offline, and cold launch is instant. And the app binary I audit is the app binary that ships. Every line of executable code in it is either mine or Apple's.
The game
It's called ForWhileDo LiRoHop. One run passes through five different games: a cloud bouncer, a space flight, a river race, a color-matching board, and a rotation puzzle where gravity always points down so you turn the room instead. Clear the fifth and the run starts again, faster and repainted. Free on the App Store, one optional unlock that gates nothing.
ForWhileDo LiRoHop on the App Store
Happy to go deep on any of this: the tone cache, the input seam, the bot calibration, or the five undocumented App Store Connect submission rules I hit on the way out the door (that one is probably its own post).
See the five worlds for yourself.
Visit the LiRoHop site