◉RORK LABJP
●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SDK 58 — Expo SDK 58 is out of beta as of 10/15 (beta since 09/15). Check your dependencies before upgrading●SDK 58 ISSUE — Early reports include Android widgets not refreshing on a schedule and Stripe linking statically●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SDK 58 — Expo SDK 58 is out of beta as of 10/15 (beta since 09/15). Check your dependencies before upgrading●SDK 58 ISSUE — Early reports include Android widgets not refreshing on a schedule and Stripe linking statically●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…
Articles/Business
⟐ Business/2026-10-21Advanced

My Launch Counter Went Up by Two on Every Launch — Why New Users' Ad-Free Grace Ended Early

New users' ad-free grace ended early because two callers each advanced the launch counter. A counter that moves once per launch, tested in Node.

launch countads3AsyncStorage11race conditionops log

✦ Premium Article

As an indie developer, I run several wallpaper apps. In one of them, new users get a short ad-free grace: no ads for their first few launches. I had set the threshold to three.

Ads started appearing sooner than that. The cause had nothing to do with the ad setup. The code that counts launches was called from both app startup and the first screen's startup, so the counter moved by two on every launch.

With a threshold of three, the counter reads 2 after the first launch and 4 after the second. The grace covered one launch, not three.

What I want to share first is that the fix lived in where the counting happens, not in any ad setting. What follows is that idea re-built for Expo (the React Native that Rork exports) and actually run on Node 22.

When "three launches" stops adding up

The arithmetic, on paper:

LaunchCounter moves by 1 per launchCounter moves by 2 (the bug)
After launch 11 (grace)2 (grace)
After launch 22 (grace)4 (ads shown)
After launch 33 (grace)6 (ads shown)
After launch 44 (ads shown)8 (ads shown)

The grace rule is only "is the counter at or below the threshold?" That rule was fine. The number fed into it was wrong.

Bugs like this are quietly annoying. Nothing crashes and no error appears. New users just get a slightly colder first week than the design intended. Whether a number is right is something you only learn by going to look at the number, so that became the first line of my checklist.

Two callers, each with a good reason

On the Android version, the counter was advanced from both the app-wide startup and the first screen's startup. Each call was written for a sound reason ("we launched, so count it"), and each looks correct alone. Only the overlap was wrong.

An Expo app can grow the same shape easily:

Where the call livesHow many times it runs per launch
Module top level (runs when imported)Depends on how many times the module is evaluated
useEffect in the root layoutNormally once; twice in development with StrictMode on
useEffect in the first screenNormally once; once more each time the screen is recreated
Right after ad SDK initializationTwice, if initialization itself is called twice

Effects also re-run when Fast Refresh picks up a saved file. If you only look at numbers on your dev machine, you will not notice how production differs.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦You can find every place in your app that advances the launch counter, and measure with a test how far it really moves in a single launch
✦You will understand why an async read-add-write ends up as +1 or +2 depending on call spacing, so you can spot the same bug before shipping
✦You can drop in a counter for AsyncStorage that moves once per launch no matter how many callers it has, and test it separately from the grace rule
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

⟐ Business2026-10-19
Before You Add Payments to Your App: Deciding What to Publish for Japan's Commercial Transactions Disclosure
Selling to users in Japan can require a disclosure page under the Act on Specified Commercial Transactions, even for individuals. Compare three ways to handle your address and phone number, and the order I decide things in.
⟐ Business2026-10-18
When Pasting the Submission Log Didn't Fix It, Read From the First Error, Not the Last Line
When a Rork App Store submission fails and pasting the log back to Rork keeps returning the same failure: a 40-line script that pulls out the first error, and how to match it against the four causes Rork's docs list.
⟐ Business2026-10-17
Can a Booking or Shop App Use Stripe? Apple Draws the Line at Where the Thing Gets Used
Whether a booking or shop app may take payment with Stripe or must use in-app purchase comes down to Guidelines 3.1.1 and 3.1.3. A table by what you sell, a small routing helper, and a three-line review note.