RORK LABJP
ENGINE — Rork Max is powered by Claude Code and Claude Opus 4.6, generating native Swift apps directlyCORE ML — Rork Max reaches on-device Core ML inference alongside HealthKit, HomeKit, NFC, and App ClipsSEED — Rork raised a $15M seed led by Left Lane Capital in April 2026, joined by Peak XV and a16z SpeedrunM&A — Rork acquired the app builder Paperline and says it will stay acquisitive to bring in engineering talentMARKET — Gartner expects 75% of new applications to be built with low-code or no-code tools by the end of 2026GROWTH — The no-code AI platform market is projected to grow from $4.9B in 2024 to $24.8B by 2029ENGINE — Rork Max is powered by Claude Code and Claude Opus 4.6, generating native Swift apps directlyCORE ML — Rork Max reaches on-device Core ML inference alongside HealthKit, HomeKit, NFC, and App ClipsSEED — Rork raised a $15M seed led by Left Lane Capital in April 2026, joined by Peak XV and a16z SpeedrunM&A — Rork acquired the app builder Paperline and says it will stay acquisitive to bring in engineering talentMARKET — Gartner expects 75% of new applications to be built with low-code or no-code tools by the end of 2026GROWTH — The no-code AI platform market is projected to grow from $4.9B in 2024 to $24.8B by 2029
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.

Rork498Expo139first 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
SharedPreferencesCan 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.

Decide "How It Should Behave" First

Desired behavior differs by state. Every time I add a piece of state, I ask myself three questions.

  1. Should this state be reset on reinstall (e.g., local drafts, UI settings)?
  2. Should this state survive reinstall (e.g., an identifier to prevent fraudulent trial re-acquisition)?
  3. Should this state be held by the server, not the device (e.g., purchases, subscriptions, accounts)?

The table below is my default assignment.

StateDesired behaviorWhere it lives
Onboarding completedFine to re-show on reinstallAsyncStorage
UI settings / themeFine to clearAsyncStorage
Free trial consumedShould survive (prevent re-acquisition)Server (SecureStore as aux)
Purchase / subscription entitlementSurvives, device-independentServer / store (StoreKit/Play)
Install identifierShould survive on the same deviceSecureStore

The key point is never judge money- or fraud-related state with device storage alone. AsyncStorage is wiped on reinstall, so putting "trial used" there lets a user re-acquire the trial infinitely by deleting and reinstalling. Rely on SecureStore alone, on the other hand, and behavior splits between iOS and Android. Money decisions should ultimately be held by the server, with device storage demoted to a cache for speed.

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-07-07
Laying Out Variable-Height Images in Two Columns: A Masonry Wallpaper Gallery in a Rork Expo App
From why numColumns cannot pack variable-aspect images cleanly, to a dependency-free column-balancing algorithm, to keeping virtualization with FlashList masonry and a pragmatic no-dependency fallback, building a wallpaper gallery with real code.
App Dev2026-07-05
Building a One-Time Code Field in Expo — SMS Autofill and Segmented Display Together
A six-digit verification screen looks trivial, but once you account for SMS autofill, pasting, and deleting one digit at a time, it needs real care. Here is how to nail the iOS and Android autofill first, then build a segmented look on top of a single TextInput that does not break.
App Dev2026-06-30
Previewing Files In-App in Rork — calling Quick Look safely from Expo
How to preview PDFs, images, and Office documents in place without sending users out of your app, using Quick Look (QLPreviewController) from Rork (Expo). Covers pre-downloading remote files, the local-URL requirement, the Android FileProvider alternative, and handling the share button.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →