DEV Community

Strobolt
Strobolt

Posted on

Three months after shipping a brain-training app on iOS alone: the restore bug, the paywall mistake, and what a niche subreddit taught me

I'm a solo iOS developer. Three months ago I shipped Dual N-Back: IQ Brain Training, a working-memory trainer built around the dual n-back task, with a "lab" of 12 short cognitive tests (chimp test, Stroop, reaction time, span tasks). No budget, no team. Here is what actually mattered, with the technical details I wish someone had written down.

1. The restore path is a retention feature, not a StoreKit detail

The bug that cost me the most was not in a feature. A paid subscriber who reinstalled the app, or restored a new phone from backup, could see the paywall on content they had paid for. Nobody writes a support email for that; they write a one-star review that says "scam".

What went wrong, in StoreKit 2 terms: I was deciding entitlement from Transaction.currentEntitlements at launch, and treating an empty result as "not a subscriber". After a fresh install that stream can legitimately be empty for a moment, and on some devices it stays empty until a restore or a network round-trip. The fix that held:

  • Keep a local cache of the last known entitlement, and only downgrade it after a confirmed negative, never on an empty first read.
  • Fall back to Transaction.all when currentEntitlements is empty; the history survives where the current view does not.
  • Put a visible Restore button on the paywall and on the settings screen, and make it call AppStore.sync() before re-reading entitlements.
  • Test the reinstall path on a real device with a sandbox account before shipping anything else. SKTestSession does not run under xcodebuild on the command line in my setup, so this has to be a manual pass.

Ship this first. Everything else in the app is invisible to someone who sees a lock on what they paid for.

2. Never take back what was free

At the end of August I put part of the free lab behind the subscription. Within two days the new reviews were all one sentence: "was free, now paid". The store ranks on rating, and at low review volume a handful of one-stars from existing users outweighs weeks of new five-stars. I reverted it, replied to every review saying so, and wrote on the store page what stays free.

The lesson is not "don't charge". It is: decide what is free before launch, say it on the listing, and never move that line backwards.

3. The niche community is your best QA, if you show up as yourself

The dual n-back subreddit is about 4,000 people who take this seriously. My first post there, as the developer, openly, got 10 upvotes and 24 comments, three of them longer than the post. Almost every change I shipped since came from that thread: the onboarding lost a forced practice session, the scoring stopped rewarding "well ignored" non-matches, the instant-feedback flashing became optional.

Their rules ban anyone posing as a regular user to promote an app, and they check. Being the developer in the open was never a problem; pretending would have been.

4. Localization outperformed every feature

The store listing is in eight languages and the app in more. A meaningful share of downloads come from storefronts outside the US, where the keyword is far less contested. What I learned doing it alone with machine translation:

  • Check the script, not just the language. My Russian and Ukrainian keywords were once in Latin characters. A one-line check for Cyrillic would have caught it months earlier.
  • Short strings flip meaning. "Second from the left" came back wrong in two languages. Flag anything under four words for a second pass.
  • Never leave a locale half done. Five of my languages were silently 100% English because interpolated strings had no key; users saw English and rated it "not localized".
  • Translation tables do not belong in Swift source. Moving 113,000 lines of dictionary literals out of the code took one of my other apps from an 8-12 minute build to 80 seconds.

5. Game Center leaderboards need a submittable version

Leaderboards referenced in code do not exist until you create them in App Store Connect, and the API refuses to release them unless there is a version in a submittable state (NO_VERSIONS_ELIGIBLE_FOR_GAME_CENTER_RELEASE). Score ranges must be sent as strings. They only go live when that version is approved. Plan this a release ahead, not the night before.

6. Say what the evidence says, including the bad news

Dual n-back is the one brain-training task with a real research record, and that record is mixed. The 2008 study reported fluid intelligence gains (https://doi.org/10.1073/pnas.0801268105); a 2014 meta-analysis found a small transfer effect (https://doi.org/10.3758/s13423-014-0699-x); a well-controlled 2013 replication found none (https://doi.org/10.1037/a0029082). What nobody disputes is that you get better at the task and at tasks like it.

Stating that plainly got me more trust from this audience than any "boost your IQ" line would have. The people who care check your sources.

What the app is, for context

Seven modes from dual to hepta n-back, N from 1 to 15, per-stream accuracy, full history, spoken letters in the user's language, and the 12-test lab with Game Center leaderboards. Free tier: the lab, the history, and three n-back sessions a day at N=1 and N=2. Pro unlocks the rest, as a subscription or a one-time purchase. SwiftUI, StoreKit 2, Game Center, no backend.

App Store: https://apps.apple.com/us/app/dual-n-back-iq-brain-training/id6775563998

Happy to answer questions on any of the above, especially the StoreKit restore edge cases, which I still think are under-documented.

Top comments (0)