Back to the Lab

The Lab · Development

NIGHTMILE Is Out: Apple Rejected It for a Leaderboard It Does Not Have

My iPhone drift racer is live. 27 days, 273 commits, two rejections, and the one about a leaderboard that does not exist taught me the most.

RAXXO Studios 8 min read
TLDR This entry in one minute

Each line jumps to its section

  • NIGHTMILE is live on the App Store: free, iPhone, portrait, and it works with the SIM pulled out
  • 27 days from first commit to live, 273 commits, and two rejections in the middle
  • Apple rejected it for uploading scores to a global leaderboard it does not have, and they were right to
  • Draw calls came down from 4,935 to 964 on the run state, which is what made it playable on an older phone

NIGHTMILE went live this morning. It is a one-thumb endless drift racer for iPhone, it is free, and it makes no network calls at all.

The interesting part is not the game. It is that Apple rejected it for a feature it does not have, and reading their rejection taught me something I had been getting wrong for years.

Apple rejected it for a leaderboard that does not exist

The second rejection came under Guideline 5.1.2, Data Use and Sharing. The stated reason was uploading users' scores to a global leaderboard without consent.

There is no leaderboard. There is no server. The shipped bundle contains zero occurrences of fetch, XMLHttpRequest, WebSocket or sendBeacon. I checked, because for about ten minutes I was convinced I had shipped something I did not write.

Then I opened the screen the reviewer had opened. NIGHT CIRCUIT shows a ranked list of racer names, one of which reads like a username, with week and month and all-time filters and a countdown that says "resets in 1d". If you had never seen the code, you would call that a global leaderboard too. I would.

The rivals are eight ghosts generated on your own device. That was written down, accurately and in detail, in the privacy policy. The reviewer was not on the privacy policy. They were on the ranks screen, where nothing said otherwise.

So the fix was one line of text, on the screen itself, saying what the thing actually is.

The lesson I keep having to relearn

A fact stated somewhere the reader is not is the same as a fact not stated.

I had done the work. The disclosure existed, it was thorough, and it was correct. It was also in Settings, which is a place nobody goes, least of all a reviewer working through a queue.

This is not really an App Store lesson. Every measurement I had said the app was clean, and every one of them was answering a question nobody was asking. The question being asked was "what does a human conclude from looking at this screen", and no grep answers that.

Before the resubmission I walked the app as a stranger and listed every screen that resembles a networked feature. Anything with a ladder, a rank, a feed, other people's names. Each one got a legible line saying what it is. Not micro-type, and not in a settings page.

The first rejection, for the record, was 2.1 App Completeness, which was an information request with eight questions attached. That one was fair too.

What it actually does

Drag anywhere on the glass to steer. Hold a second finger for nitro. Chain drifts for a combo multiplier and thread traffic for near-miss bonuses. There is no finish line, only your last number.

Four decisions are worth stating because they are unusual enough to be checkable rather than marketing.

It makes zero network calls. No account, no sign-up, nothing leaves the phone. Every run works in airplane mode, underground, or on a plane, and your progress lives on the device.

There is no advertising in 1.0, and that was decided before the store was built. Six optional one-off purchases exist, no loot boxes and no randomised crates, and every item shows its price and its contents before you tap.

The four soundtracks are generated on the device while you drive. No audio files ship in the download, which is also why the download is small.

Reduced Motion damps the camera shake and stops every flashing effect, and it turns itself on if Reduce Motion is already on in iOS. That was built for photosensitivity rather than added as a settings row.

The number that made it playable

The performance work has one figure worth quoting. On the run state, draw calls went from 4,935 to 964. On the nitro state, 5,203 to 1,022.

That is roughly an eighty percent cut on the two states that matter, and it is the difference between a game that runs on a current phone and one that runs on the phone somebody actually has.

Most of it came from merging things that had no business being separate draws. Fourteen identical boxes on one material that spin as a rigid assembly do not need fourteen calls. Neither does a wheel whose rotor face, rotor edge and hat all share a material and never move relative to each other.

One honest caveat, because I nearly wrote it wrong. There is a further cut, from 921 to 685, that came from merging the car's shell after build 11 was already with Apple. It is real and it is measured and it is not in the version you can download today. It ships in 1.0.1.

The whole engineering story, including a floating point bug that made the entire city walk sideways off the road, is in Shipping a Three.js Game to the iOS App Store.

The soundtrack is code, and that has a cost

The no-network rule pushed the audio somewhere I did not expect. Nothing streams, so the choice was ship audio files or generate the music on the device. I generate it.

Four stations, each with its own character. Classic synthwave, a warmer cruise station, a heavier bass-led one, and an ambient one built out of echoes. Every note is produced live while you drive.

What I got out of that was mostly install size. Four stations of listenable length as audio files would have been the largest thing in the download by a wide margin. As code it is close to free. It also never repeats the way a loop repeats, because there is no second pass to notice.

The cost is real and I would rather name it than pretend. You cannot hum a procedural track before you hear it. There is no version you sit down and compose, no eight bars you play back and adjust. You tune ranges and rules, then listen to what the rules did. That loop is slower and much less certain than writing music, and if the result is wrong you often cannot tell which knob did it.

Worth it here because the offline constraint made it nearly mandatory. I would not choose it for a game that was allowed to ship a soundtrack.

27 days, and what that number hides

First commit was 6 August. Live on 2 September. 273 commits in between.

I am wary of quoting that as a speed claim, because the number flatters the wrong thing. Roughly a third of those commits are the two rejections and the work around them, and none of that was building a game. A meaningful slice more is tooling that exists only because I was one person doing every job: screenshot generation, a headless recorder, an economy simulator, a release tool that talks to App Store Connect so I never had to trust a form.

The actual game took less time than the apparatus around shipping the game. That is the honest shape of a solo release and it is not what a 27 day headline suggests.

The economy is the clearest example. Before tuning any number I built a simulator, ran a thousand virtual players for thirty days each, and found my payout curve was concave, meaning a run twice as good paid less than twice as much. That is exactly backwards for a skill game. I would not have found it by feel, and I have written before about why I measure before balancing rather than trusting my own judgement on my own work.

Bottom Line

NIGHTMILE is free on the App Store now, for iPhone, iOS 15 and later. It plays in portrait with one thumb and it works with no connection at all.

If you drive it and it holds your attention for more than one run, a rating genuinely changes what happens next. A store page with no ratings converts badly and ranks nowhere, and the first twenty are the hardest to get. That is the whole ask.

If something breaks, or the difficulty feels wrong, or a screen confuses you the way the ranks screen confused a reviewer, tell me. help@raxxo.shop reaches a person, and 1.0.1 is already open.

The product page is at raxxo.shop/pages/nightmile, and the rest of the build writing lives in the Lab Overview.

Get the next entry by mail
One mail when a new entry lands. No spam. Unsubscribe anytime.
RAXXO Studios

Written by

RAXXO Studios

One designer in Berlin, close to twenty years in. I build tools with AI, use them daily, and write down what happened.

Share this entry

X LinkedIn
All entries