App Development BKM
Best known methods for shipping a React Native + Expo iOS app from idea to App Store.
preamble
What this is.
This is a phase-based reference for shipping a React Native + Expo iOS application from idea to the App Store and through the post-launch stabilization period. It is written for solo founders and small teams who do not have a dedicated DevOps engineer, a release manager, or an iOS specialist on staff.
The document is organized into six phases. Each phase has a brief intro that names what the phase is about, a checklist of operational steps that should be completed before moving to the next phase, and warnings drawn from lessons learned the hard way on previous shipping cycles. The warnings are deliberately separated from the checklists. Checklists are what to do; warnings are what to avoid.
This document does not cover business operations (LLC formation, banking, tax). It does not cover supervising agentic AI development tools — that surface lives in the AI Working Instructions. It does not cover the reflective lessons from specific shipping cycles — those live in Field Notes. This is the operational app-development reference, scoped to that.
The document is versioned and dated. New best-known methods are added as new shipping cycles produce them. Existing methods are revised when subsequent practice reveals they were incomplete. The version log at the bottom records what changed.
- Phase 0 — Pre-flight— before you write a line of code
- Phase 1 — Project initialization— repo, environment, conventions
- Phase 2 — Build phase— feature work, integration, on-device testing
- Phase 3 — Monetization— RevenueCat, paywall, subscription products
- Phase 4 — App Store submission— listing, screenshots, reviewer notes
- Phase 5 — Post-launch and iteration— first 30 days and beyond
phase 00
Pre-flight.
Before any code is written, several decisions and accounts need to exist. Skipping this phase is the most common cause of multi-week delays mid-project, because the missing prerequisites only surface when you need them and the lead time on each is non-trivial.
Account prerequisites
Each of these takes between hours and weeks to set up. Start them in parallel as early as possible.
- Apple Developer Program account — $99/year. Individual or Organization. Verify enrollment is fully active and the agreement is signed.
- Apple ID for the account holder — should be one you control long-term, not a personal email tied to another job.
- Expo account — free to start. Required for EAS builds.
- EAS subscription — free tier works for early development; production builds and TestFlight pushes typically require a paid plan ($29/month at this writing).
- RevenueCat account — free for the first $2,500/month of revenue. Required if shipping any in-app purchase.
- GitHub or other private repo host — private repository. Public is acceptable only if there is zero sensitive code or keys.
- Domain — one for your app's privacy policy and terms URL. Apple requires public-facing privacy URLs for any app collecting data.
Naming convention decisions
Names made early ripple through every system. Decide them once, write them down, and do not change them mid-project.
- App name — exactly as it will appear on the App Store. 30 character limit.
- Bundle ID — reverse domain notation:
com.yourcompany.yourapp. Permanent. Cannot be changed after first App Store submission. - Subscription product IDs — convention:
com.yourcompany.yourapp.premium.monthlyand.premium.yearly. Permanent once published. - RevenueCat entitlement identifier — lowercase, no spaces.
premiumis the convention. Anything with capital letters or spaces invites silent failures later. - Subscription Group name — user-facing label for grouping related subscriptions (e.g. "App Premium").
- Privacy URL — e.g.
https://yourdomain.com/privacy. - Terms URL — e.g.
https://yourdomain.com/terms. - Support email — single canonical address. Avoid multiple support addresses for one app.
Identifier drift between code and dashboard ships silent bugs. If your code looks up an entitlement called premium and your RevenueCat dashboard has it as Premium or App Premium, real subscribers will pay successfully and never receive Premium features. The string must match exactly. Pick the convention at project start, document it, and verify the match every time you touch either side.
Product decisions before code
- Free tier definition — what the user gets at $0. Specific, quantifiable.
- Premium tier definition — what unlocks at the paid tier. Specific, quantifiable.
- Pricing — monthly and yearly tiers. Trial duration if any.
- Trial mechanics — most apps offer a 7-day yearly trial. Apple handles the trial flow, but you decide the length and which products it applies to.
- Restoration UX — every paid app needs a "Restore Purchases" button accessible from settings. Apple requires this.
- Plan defaults — which plan is preselected on the paywall (typically yearly for revenue, monthly for trial-to-paid funnel).
phase 01
Project initialization.
The first commits to the repo set conventions that affect every later session. This phase establishes the structure, the file conventions, and the build pipeline. Decisions made here are expensive to reverse.
Repository structure
- Initialize as private repo. Even if you intend to open-source eventually, start private. You can flip later; you cannot un-leak credentials committed to a public repo.
- Use Expo's managed workflow via
npx create-expo-appunless you have a strong reason to use bare React Native. - Standard directory structure —
mobile/for app code,docs/for project documentation,scripts/for build/deploy automation. - Single-file vs multi-file decision — for early-stage solo apps, a moderately large single
index.tsxis acceptable. Past 5,000 lines, start splitting. Past 15,000 lines, you have a refactor debt to track. - No backup files in the working tree. Use git branches for experiments.
.bak,.old, and numbered backup files in working directories cause silent confusion later.
Backup files are an attractive nuisance for any tool that operates on file paths. If your AI development tool, CI script, or build process can encounter a .bak file in the same directory as the live file, eventually it will operate on the wrong one. Move backups to a separate directory or rely entirely on git history.
Environment configuration
- Maintain three env files —
.env(local development),.env.production(production builds), and.env.example(committed template with placeholder values). - Decide whether .env files are committed — solo private repos can commit them with reasonable security; team or public repos should never commit and use EAS Secrets / Expo environment management instead.
- Document the env variable convention — Expo requires the
EXPO_PUBLIC_prefix for client-side variables. Without that prefix, the variable isundefinedat runtime even though it shows up locally. - Variable parity — every variable in
.envmust also be in.env.production, even if values differ. - Validate at runtime — log
!!process.env.YOUR_VARIABLEat app startup to confirm the variable is loaded. Skip this step at your peril.
Missing the EXPO_PUBLIC_ prefix is a silent failure. The variable exists, the build succeeds, the code reads it as undefined. Document the prefix requirement in your .env.example file and verify all client-readable variables follow the convention.
Build pipeline
- Configure
eas.jsonwith three profiles minimum:development,preview(TestFlight internal), andproduction(App Store). - Run a successful EAS build to TestFlight before any feature work. Confirms the build pipeline is functional with an empty app.
- Set up Expo Application Services credentials — let EAS handle Apple credential generation when prompted. Do not manually manage certificates and provisioning profiles unless you have specific reasons to.
- Verify code-signing works end-to-end with an empty app before adding complex features.
- Document the build commands in a README or scripts file:
eas build --profile development --platform ios, etc.
Project documentation
- README with run instructions — anyone (including future you) should be able to clone and run in under 30 minutes.
- Naming conventions reference — bundle ID, product IDs, entitlements, in one place.
- Account inventory — which accounts the app depends on (Apple, RC, EAS, etc.).
- Decision log — major product and technical decisions with rationale. Saves re-litigation later.
phase 02
Build phase.
The longest phase. Most of the visible work happens here. The disciplines that matter most in this phase are the ones that prevent you from accumulating technical debt that surfaces during submission.
Development discipline
- Build to a real device weekly, minimum. Simulator and preview tabs lie. Real device renders are the only ground truth.
- Capture screenshots of major UI surfaces as you build them. Some will become App Store screenshots later. Some will become reviewer-facing diagrams. None of them are easy to recreate after the fact.
- Test in airplane mode periodically. Any feature that depends on network access has failure modes that only surface offline.
- Use TestFlight internal testing from week 2 onward. Push a build per week minimum. Helps you catch regressions before they accumulate.
- Run on multiple iPhone sizes. 6.9" (Pro Max) and 5.4" (mini, if still relevant) at minimum. UI bugs on edge sizes are common.
Code quality controls
- Lint and type-check on every commit. TypeScript strict mode if at all possible.
- Commit small and often — single logical change per commit. Makes git bisect possible later.
- Tag your TestFlight builds with version numbers in App Store Connect, even pre-release.
- Maintain a changelog — even a casual one. You'll need it for App Store release notes.
Localization (if shipping multi-language)
- Decide between centralized i18n files vs inline strings early. Centralized is correct long-term but requires more upfront setup. Inline ternaries are tempting but become unmaintainable past a few dozen strings.
- RTL languages require explicit testing. Arabic, Hebrew, Persian. Layout bugs are common; the simulator handles RTL but real-device behavior sometimes differs.
- Native speaker review before submission. Machine translation is a starting point, not a shipping point.
- Test with longer translations. German, Russian, and Arabic strings often run 30-50% longer than English. Buttons that fit English text may overflow in other languages.
Inline strings everywhere is fast in week 1 and a tax in week 8. If you suspect the app will support multiple languages, set up i18n infrastructure on day one. Migrating inline ternaries to a centralized translation file mid-project is significantly more work than starting with the right structure.
Crash reporting and analytics
- Integrate Sentry, Bugsnag, or equivalent before submission, not after. You will not catch the first user crash on TestFlight without it.
- Configure source maps so stack traces are readable.
- Add analytics events for paywall presented, plan selected, purchase completed, restore initiated. These are the funnel you'll want to optimize post-launch.
- Log RC events — at minimum, configure success and entitlements-active events at app launch.
phase 03
Monetization.
RevenueCat integration and paywall implementation. This is the phase where small misalignments cause the largest downstream pain. Every identifier must match across code, App Store Connect, and the RC dashboard.
RevenueCat dashboard configuration
Do this before code. Configuration drives the code, not the other way around.
- Create the iOS app in RC dashboard with the exact bundle ID from your App Store Connect listing.
- Create products matching App Store Connect IDs exactly. Type: Auto-Renewable Subscription.
- Create the entitlement with identifier
premium(lowercase, no spaces). Display Name can be branded. - Attach both products to the entitlement.
- Create the offering identified as
default. Mark as Current. - Add packages using the predefined
$rc_annualand$rc_monthlyidentifiers from the dropdown. Custom identifiers breakpackageType-based filtering downstream. - Paste the App-Specific Shared Secret from App Store Connect into RC's iOS app settings.
- Get the iOS API key (starts with
appl_) and document where you stored it.
App Store Connect subscription setup
- Create subscription group with branded name.
- Create both subscription products with exact IDs matching RC.
- Set duration (1 month, 1 year).
- Set pricing for all territories you will ship to.
- Tax category — App Store Software for most consumer apps.
- Family Sharing — typically ON for consumer apps.
- Localized Display Name and Description for at least one language, ideally all you ship.
- Trial offer on yearly subscription (typically 7 days). Apple handles trial flow but you configure it here.
Code integration
- Install both packages —
react-native-purchasesfor the SDK, andreact-native-purchases-uiif using RC's native paywall. - Configure RC at app launch —
Purchases.configure({ apiKey: RC_API_KEY }). - Implement entitlement check —
customerInfo.entitlements.active['premium']. The string must match RC dashboard exactly. - Implement purchase, restore, and trial-eligibility helpers.
- Add a guard around paywall presentation — check
RC_CONFIGUREDbefore callingpresentPaywallorpurchasePackageto avoid crashes if API key is missing or RC failed to initialize. - Wrap purchase calls in try/catch with user-facing fallback messages.
- Verify entitlement identifier consistency across all files — lowercase
'premium'everywhere, no exceptions.
The custom paywall vs SDK paywall decision is bigger than it looks. RevenueCat's RevenueCatUI.presentPaywall() is a complete native paywall with offerings fetching, plan toggle, purchase flow, and trial mechanics built in. A custom paywall gives you full design control but requires implementing offerings fetching, package selection, purchase wiring, and trial logic yourself. For v1 of any new app, ship with the SDK paywall. Custom comes in vN+1 if needed.
Sandbox testing
Before any TestFlight submission, every purchase path must be verified end-to-end with sandbox credentials.
- Create at least one sandbox tester account in App Store Connect → Users and Access → Sandbox Testers.
- Sign in to sandbox account on your iPhone via Settings → App Store → Sandbox Account.
- Verify offerings load — check that real prices appear in the paywall, not "Products unavailable."
- Test a yearly purchase with trial — confirm trial flow appears.
- Test a monthly purchase without trial — confirm direct purchase flow.
- Test plan toggle — yearly to monthly and back.
- Test restore purchases on a fresh install.
- Test cancellation flow — sandbox subscriptions auto-cancel after a few minutes; verify the app reflects loss of premium correctly.
- Test purchase from each paywall trigger point. If your paywall is triggered from 5 different places in the app, test all 5.
phase 04
App Store submission.
The bottleneck phase. Apple's reviewer has limited context and limited time. Every artifact you provide either speeds the review or invites questions. The disciplines here are about making the reviewer's job as easy as possible.
Pre-submission checklist
- App icon at all required sizes — Apple's automated tools handle most sizes if you provide the master.
- Launch screen / splash screen works at all device sizes.
- App version bumped — typically increment
CFBundleShortVersionStringinapp.jsonfor user-facing version. - Build number bumped — must be unique for every TestFlight push.
- Production build via EAS with the production profile.
- Smoke test on TestFlight production build — install via TestFlight (not direct EAS dev build), verify everything works.
- Verify
.env.productionvalues are what shipped — same RC API key, same external service URLs as you tested.
App Store listing
- App name — 30 character limit, includes any subtitle suffix you want visible in search.
- Subtitle — 30 character limit, appears below the name.
- Promotional text — 170 character limit, can be updated without re-review (use this for "what's new" framing).
- Description — 4,000 character limit. Front-load value proposition. Apple reviewers read this carefully.
- Keywords — 100 character limit, comma-separated. Appear in search ranking, not visible to users.
- Support URL — must be a real reachable page.
- Marketing URL — optional but recommended.
- Privacy URL — required for any app collecting data.
- Category — primary and optional secondary.
- Age rating — 4+ for most consumer apps.
Screenshots
- Required iPhone sizes — 6.9" (1290×2796) and 6.5" or 6.7" (1284×2778). Apple often accepts the larger size for both, but providing both is safer.
- Capture from real device, not simulator. Volume Up + Side button. Direct upload, do not crop or resize.
- 3-10 screenshots minimum. First three matter most — visible in search results.
- Lead with value, not features. The first screenshot should answer "why would I install this?"
- Optional captions/text overlays — many apps add explanatory captions over screenshots. Useful but not required.
Review Information
This is the section the reviewer reads to understand your app. Every field matters.
- Demo account — credentials for an account the reviewer can use to access all paid features without making a real purchase.
- Notes for reviewer — describe how to trigger the paywall, what features are gated, what the test purchase will look like. Be specific.
- Subscription review screenshot — a real-device screenshot of the paywall showing real prices. Required for every subscription product.
- Contact information — must be reachable. Apple may email with questions.
- App-specific notes for sensitive features — if your app uses AI/LLM features, religious or political content, health information, or anything that might confuse a reviewer, address it directly in the notes.
Reviewer notes that explain the AI guardrails save rejection cycles. If your app uses an LLM-powered feature, the reviewer will encounter it. Explain in the notes: what the AI can and cannot do, what content guardrails exist, what happens on out-of-scope queries, and any fact-checking or safety measures. The reviewer's first instinct on encountering AI features is to test edge cases. Pre-empt the test by describing the constraints.
Final submission
- Verify all "Missing Metadata" warnings are cleared. Every red dot in App Store Connect must be resolved before submission.
- Verify the build is selected — under Build section of the version page, the production build must be chosen.
- Verify export compliance — most apps qualify for "uses standard encryption" exemption.
- Verify paid agreements active — Paid Apps Agreement, banking, tax forms.
- Submit for review.
- Status moves to "Waiting for Review" typically within an hour. "In Review" within 24-48 hours. Verdict typically within 24 hours of "In Review."
phase 05
Post-launch and iteration.
The phase that determines whether you ship a hit or a launched app that quietly fails. The first 30 days are the highest-information period of the entire project. Discipline here is what turns a v1 into a v2.
The first 72 hours
- Monitor App Store Connect for review verdict. Approval typically comes within 24-48 hours.
- Watch crash reports in your observability tool (Sentry, Bugsnag).
- Watch RC dashboard for first real subscriptions. Verify the entitlement actually activates Premium for real users.
- Watch user reviews on the App Store. Respond to early reviewers within 24 hours where possible.
- Be ready for a v.0.1 hotfix. First-week bugs are normal. Pre-decide what would constitute a hotfix-worthy issue.
The first 2 weeks
- Track key funnel metrics — installs, paywall views, conversion rate, trial starts, trial-to-paid conversion.
- Identify the largest drop-off in the funnel. That's your first iteration target.
- Capture lessons from launch while they're fresh. What surprised you, what broke, what worked better than expected. These become inputs to the next BKM revision.
- Do not start major refactors in the first two weeks. Stability matters more than improvement.
- Plan v.1 vs v.0.1 split — what's a hotfix, what waits for the next minor release.
v.0.1 hotfix discipline
- Hotfixes are for critical bugs only — crashes, broken purchases, data loss. Polish waits.
- Hotfix scope is one issue at a time. Do not bundle features into hotfixes.
- Test the hotfix thoroughly before submitting. Apple's review tolerance for buggy hotfixes is low.
- Document the hotfix in your changelog and version notes.
Beyond week 2
- Plan vN+1 based on data — funnel optimization, user feedback, requests.
- Custom paywall consideration — if your SDK paywall is converting poorly, that's the trigger for a custom paywall investment.
- Localization expansion — if a non-English market is showing organic traction, prioritize that language.
- Feature deprecation discipline — what to remove, when. Apps that only add features become bloated.
- Submit your first major update on a schedule — typically 4-6 weeks after launch, not earlier.
cross-phase reference
Standing reference tables.
Identifier conventions
| Identifier | Format |
|---|---|
| Bundle ID | com.company.app |
| Subscription product (monthly) | com.company.app.premium.monthly |
| Subscription product (yearly) | com.company.app.premium.yearly |
| RC entitlement | premium (lowercase) |
| RC offering | default (mark as Current) |
| RC packages | $rc_annual, $rc_monthly (predefined) |
| Env variable for RC iOS key | EXPO_PUBLIC_REVENUECAT_IOS_API_KEY |
Required URLs
| Purpose | Notes |
|---|---|
| Privacy Policy | Required by Apple. Must be publicly reachable. |
| Terms of Service | Required for any subscription app. |
| Support URL | Required. Can be your domain or a hosted contact form. |
| Marketing URL | Optional but recommended. |
Cost reference
| Service | Approximate cost |
|---|---|
| Apple Developer Program | $99/year |
| EAS (Production tier) | $29/month |
| RevenueCat | Free up to $2,500/month revenue, then percentage |
| Sentry / Bugsnag (free tier) | Free for low-volume apps |
| Domain | $10-20/year |
version log
What changed and when.
Every revision of this document is recorded here. New phases or sections are noted. Existing methods are revised when subsequent shipping cycles produce better practice.
- Initial publication. Six phases (Pre-flight, Project initialization, Build, Monetization, Submission, Post-launch). Drawn from a complete iOS shipping cycle in May 2026.
related references
Where the rest of the operator framework lives.
- AI Working Instructions — disciplines for supervising agentic AI development tools (Cursor, Claude Code, Vibe Code, etc.) on production-bearing work.
- Field Notes — reflective writing on specific failure modes and lessons from individual shipping cycles. Where the BKM rules earn their place.