◉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 — Beta since 09/15 with no stable date yet; running projects in Expo Go has required login since 09/03●WIDGET — A write-up traces an iOS widget that renders black only in production builds●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 — Beta since 09/15 with no stable date yet; running projects in Expo Go has required login since 09/03●WIDGET — A write-up traces an iOS widget that renders black only in production builds●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/App Dev
◇ App Dev/2026-06-25Advanced

Why Reinstalling Users Don't Return to 'First Run' — A First-Launch and State-Persistence Design for Rork (Expo) Apps

How to fix broken first-launch detection, onboarding, and free-trial state on reinstall in a Rork-generated Expo app, by treating it as an asymmetry in storage persistence. Covers what survives uninstall on iOS vs Android, separating install and version axes, and server-authoritative entitlements, with implementation.

Rork585Expo219first launchreinstallKeychain3AsyncStorage10state persistencetrial

✦ Premium Article

Why Reinstalling Users Don't Return to "First Run"

One day I got a message about one of my indie apps: "I switched phones and onboarding never showed." Normally you worry about the opposite — onboarding showing again on reinstall is the more common complaint. Digging in, the cause was that each storage type differs in whether it's wiped or kept on uninstall. The flag I used for first-launch detection was wiped on one device and survived on another.

What clicked then was that reinstall bugs are less bugs than the result of the developer not grasping the asymmetry of storage persistence. This article tackles that asymmetry head-on and shares the design — with implementation — for keeping first-launch detection, onboarding, and free trials from breaking on reinstall.

What Is "Wiped" and What "Survives" on Uninstall

Get the facts straight first. When an Expo/React Native app is deleted, what's wiped and what survives differs by storage and OS. iOS Keychain in particular is counterintuitive: items can survive even after the app is removed.

StorageAfter uninstall on iOSAfter uninstall on Android
AsyncStorage / MMKV / filesWipedWiped
SecureStore (Keychain)Can surviveWiped
SharedPreferences—Can be restored via auto-backup
iCloud Keychain synced itemsSurvive across devices—
Server-side accountSurvives (device-independent)Survives (device-independent)

This table explains nearly all reinstall behavior. Put a first-launch flag in AsyncStorage and it's wiped on reinstall, so onboarding shows again. Put "trial consumed" in SecureStore and on iOS it survives deletion, so the trial doesn't come back after reinstall (whether you want it back is a design decision). On Android SecureStore is wiped too, but SharedPreferences can be restored depending on the auto-backup setting.

In other words, "wiped or kept" is a property determined by your storage choice — not chance. The design starts here: for each piece of state, decide up front whether it should be cleared or survive on reinstall, then place it in the storage that matches.

✦

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
✦Lays out the asymmetry between storage that is wiped on uninstall and storage that survives — per iOS and Android — and shows structurally why first-launch detection breaks on reinstall
✦A durable install-identity implementation that never confuses fresh/update/reinstall, plus server-authoritative trial and entitlement checks — the exact shape I run on my own indie apps
✦Treats first launch (install axis) and post-update What's New (version axis) as separate axes, with Android auto-backup pitfalls and a migration checklist in one place
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

◇ App Dev2026-09-18
The three lines I check when prebuild stops on the standard SDK 57 Swift AppDelegate error
Turning on ios.enableSceneSupport for iOS 27 can stop prebuild cold. The cause was not my SDK version but the shape of AppDelegate.swift. Here are the three lines to check, and why copying the SDK 58 migration steps by hand will break your build.
◇ App Dev2026-09-16
The Token I Deleted Came Back After a Restart — Detecting Failed SecureStore Deletes
On Android, getItemAsync can report null right after sign-out while the value is still sitting on disk. Here is how I rewrote a sign-out path that was verifying deletion by reading instead of by the delete result.
◇ App Dev2026-09-10
Your First EAS Workflow in a Rork Repo, and the Alert That Goes Missing Exactly When You Need It
Putting two files into .eas/workflows in a repo exported from Rork, and why a notification wired with needs stays silent on exactly the nights it fails — with the output of a small local checker I actually ran.