Feature Flags

Feature Flags for Mobile Apps: A Practical Guide

Feature flags for mobile apps: kill switches, staged rollouts, remote config caching, and how to dodge App Store review pitfalls on iOS and Android.

Abstract illustration of a smartphone silhouette surrounded by geometric toggle switches in dark green and teal, some fully engaged and some outlined, representing a staged feature flag rollout

Feature flags for mobile apps solve a problem native web development does not have: once a build ships to the App Store or Google Play, you cannot edit it, you can only wrap around it. A feature flag already shipped inside a reviewed binary turns a piece of behavior on or off from your server, in minutes, without a new submission, a new review, or a new install. That single property, changing behavior without shipping new code, is what makes flags the backbone of kill switches, staged rollouts, and remote config on iOS and Android.

This guide stays specifically on the mobile side of that story: why app store review lag makes a “just push a hotfix” mindset unworkable, how a kill switch actually behaves on a device that already has the old code installed, the real difference between an app-store staged rollout and a flag-controlled one, how Firebase Remote Config, LaunchDarkly’s mobile SDKs, ConfigCat, and Unleash handle caching and offline devices differently, and the review and fragmentation pitfalls that catch mobile teams who copy a web playbook without adjusting it.

Why native apps cannot hotfix like a web page

On the web, a deploy is a git push away from every visitor. On mobile, a code change has to survive a pipeline the web has no equivalent of: build the binary, submit it, wait for human or automated review, and then wait again while the store gradually hands the update to installed devices. Apple itself publishes that, on average, 90% of submissions are reviewed in less than 24 hours, on its App Review page. That is an average rather than a commitment, and the wording itself leaves 10% of submissions outside that window with no stated ceiling. Google Play documents its side even less deterministically: updates to existing apps are processed and published as soon as possible, but certain apps are subject to extended reviews, which can take up to 7 days or longer in exceptional cases, according to the Play Console Help Center. Neither store promises a turnaround you can rely on in the middle of an incident, and that is precisely the point.

Even after approval, the update does not reach everyone instantly. Apple’s own App Store Connect documentation describes a phased release that spreads a version update over 7 days in fixed daily steps: 1%, 2%, 5%, 10%, 20%, 50%, and 100% of eligible devices with automatic updates enabled. Google Play’s staged rollout works on the same principle but gives the developer direct control of the percentage, adjustable in increments from 1% up to 100%, and it can only be used on updates, not on a first submission.

Release lag: a new binary versus a remote flag flipA web deploy goes live instantly. A native code change needs a build, a store review of one to seven days, then a phased rollout of up to seven more days before it reaches every device. A flag already shipped inside an approved binary can be flipped for every device already on that binary in minutes, with no store involved.day 0day 7day 14Web deploylive to everyone in minutesNew app binary (code change)buildreview 1-7dphased rollout to 100%, up to 7 more daysFlag inside an already-approved binarytoggled in minutes, for every device already on that build
Review and phased rollout apply to a new binary. A flag already living inside a binary that already passed review moves independently of both, which is the entire point of using one.

That gap between “I want this off right now” and “the fix has to go through the store again” is exactly what a feature flag exists to close. Nothing about a flag skips review for new code, it simply moves the decision of what to show into a switch the store already approved, so flipping it later does not require going back through the pipeline above.

What a flag actually changes on a device that already has the old code

A mobile feature flag does not download new code. Apple’s Guideline 2.5.2 is explicit that an app may not “download, install, or execute code which introduces or changes features or functionality of the app,” with a narrow exception carved out for educational apps that teach or let students test code, and only if the source stays fully visible and editable by the user. What a flag changes instead is a boolean, a number, or a small JSON payload that a conditional already compiled into the app reads and branches on. The code for both the “off” and “on” paths shipped together in the same reviewed binary; the flag only decides, at runtime, which path executes for a given device.

That distinction is also what keeps flag-driven behavior on the right side of Guideline 2.3.1. Apple’s guidance, echoed on Apple’s own developer forums, is that a feature can be turned on remotely after release as long as it was disclosed to App Review and made available for them to test before approval, ideally through a documented back door in the review notes. A flag that quietly reveals an undisclosed feature after the app clears review is the exact discrepancy between what reviewers saw and what users later experience that 2.3.1 exists to catch.

Kill switches: the mobile-specific superpower

A kill switch is a flag whose entire job is to instantly disable a feature, or an entire third-party dependency, without a new release. On mobile this matters more than on the web precisely because of the release lag above: if a payment SDK misbehaves, a map provider goes down, or a new screen crashes on a specific device class, waiting for an expedited review, even Apple’s fastest emergency path, is measured in hours at best. A kill switch already wired into the code before the incident happens turns that response time into minutes.

The same rollout mechanism that powers a kill switch also powers a gradual rollout, just run in the opposite direction: instead of expanding exposure from 1% toward 100%, an incident collapses it from wherever it sat straight back to 0%, for every device already running the binary, independent of what percentage the app store itself had reached in its own phased release.

A flag-controlled rollout can move in both directionsFour concentric rings represent a flag rollout expanding from 1 percent to 10, 50, and 100 percent of devices already on an approved binary. Unlike an app store phased release, this percentage can also be pulled straight back to zero at any point, which is what a kill switch does during an incident.1%10%50%100%expandkill switch: back to 0% in minutesno storereview neededin either direction
The same percentage lever used to expand a rollout is the one that collapses it during an incident. Neither direction touches the app store, because the code for both states already shipped in the approved binary.

Two rollout percentages, controlled by two different parties

This is the part that trips up teams moving from web to mobile: a single app can have two independent rollout percentages running at the same time, and confusing them leads to reading the wrong signal. One belongs to the store; the other belongs to your own backend.

App-store staged rollout Feature-flag rollout
Who controls the percentage Apple or Google (Apple sets it automatically over 7 days; Google lets you set it manually) Your own backend or flag vendor’s dashboard
What it gates Which devices receive the new app binary at all Which behavior is active inside a binary that already has all the code
Can it move backward instantly? Only by pausing further distribution; devices that already updated keep that binary Yes, a kill switch can drop exposure to 0% for already-updated devices in minutes
Typical timeline Apple: fixed 1%, 2%, 5%, 10%, 20%, 50%, 100% over 7 days. Google: developer-chosen increments from 1% to 100% Minutes to days, entirely at the operator’s discretion
Tied to an app version? Yes, by definition No, it can apply across every version still capable of reading that flag

A common mistake is reading a metric change during a rollout without knowing which of these two percentages was moving at the time. If the app-store phased release is still climbing, you are also mixing in users who updated at different points and may be running slightly different code paths beyond the flag itself, which is a confound the flag change alone does not explain.

Remote config tools: how the major mobile options actually differ

“Remote config” and “feature flag” are used almost interchangeably on mobile, but the tools behind them differ in hosting model, native experimentation, and, critically, how they behave when a device has no network connection.

Tool Model Native A/B read Offline / cache behavior
Firebase Remote Config Managed, part of Google’s Firebase suite Via Google Analytics and Firebase A/B Testing, layered on top of Remote Config values Default production fetch interval is 12 hours; fetching more often than the configured throttle window returns a client-side exception rather than a fresh value, and the SDK always evaluates from the last successfully cached config first
LaunchDarkly mobile SDKs (iOS/Android) Commercial SaaS, streaming connection when the app is foregrounded Native, frequentist and bayesian, built into the same platform Streams updates in real time while the app is in the foreground; the iOS SDK does not support background fetch at all, so backgrounded iOS apps do not receive live updates, while Android automatically falls back to polling in the background
ConfigCat Managed SaaS with a CDN-backed config, SDKs for iOS, Android, and Kotlin Multiplatform Not native; ConfigCat’s own docs focus on flag delivery, leaving the statistical read to an external tool Three explicit polling modes (auto polling, lazy loading, manual polling) plus a dedicated offline mode that serves only from a pre-populated local cache and never calls the network
Unleash (with Unleash Edge) Open source at the core, self-hosted or Enterprise cloud; Edge is a caching, evaluation-at-the-edge layer in front of the SDKs Not native; Unleash’s own guide walks through bucketing plus impression events, then hands the significance read to an external analytics tool Edge evaluates locally and, per Unleash’s own documentation, a single instance can serve tens of thousands to hundreds of thousands of requests per second from cache, with streaming mode narrowing replication lag from a polling interval down to near real time

None of these four is universally right for a mobile app. A team already inside the Firebase or Google ecosystem gets remote config, analytics, and a basic experiment read without adding a vendor. A team that wants a mature, native statistical engine and is comfortable paying for it tends to land on LaunchDarkly. A small team that wants a lean SDK and predictable polling behavior without deep platform lock-in often prefers ConfigCat. A team that already runs Unleash for its web services and wants the same flags in its mobile app, without sending end-user data further upstream than necessary, is the one Unleash Edge is built for.

Cache and offline behavior: the part most guides skip

Every one of the tools above shares one non-negotiable rule: a mobile SDK can never block the UI waiting on a network call it might not get to finish. A device can lose connectivity mid-flight, sit in airplane mode, or simply cold-start before a fetch resolves, and the app still has to render something.

How a mobile flag SDK resolves a value at app startAt cold start the app checks whether a flag value is already cached on the device. If it is, the app renders instantly with that cached value. If not, it requests the latest value from the remote config service; on success it stores the new value and applies it, and on failure or offline it falls back to the default value hardcoded in the binary.App cold startevaluate flagcached ondevice?yesRender instantlywith the cached valueno / expiredRequest latest valuefrom the remote config serviceSuccessstore, applyOffline / timeoutfall back to thebinary’s default
The right-hand branch of this diagram is the one most mobile flag bugs come from. Every flag needs a hardcoded default in the binary itself, because a network fetch is never guaranteed to complete before the screen has to draw.

The tools above encode this rule in different but compatible ways. Firebase Remote Config throttles repeated fetches (five requests per hour on older SDK versions, more permissive on newer ones) and only replaces the in-memory config after a fetch plus an explicit activate call, so a slow or failed fetch never blocks rendering, it just means the app keeps using whatever it already had. LaunchDarkly’s mobile SDKs keep a persisted store on-device so a cold start already has last-known values before any network round trip completes. ConfigCat’s offline mode takes this to its logical extreme: an app can run entirely against a locally seeded cache and never call the network, useful for a build that needs to work in a genuinely disconnected environment. In every case, the discipline that matters is the same: ship a sane default for each flag inside the binary, because the network is the one thing none of these tools can guarantee.

Combining feature flags with a real A/B test on mobile

A flag rollout tells you what percentage of devices see a behavior. It does not, by itself, tell you whether that behavior improved anything. As covered in more depth in our guide on feature flags versus A/B testing, a flag is a delivery mechanism and a test is a measurement method, and mobile does not change that boundary, it just adds two extra wrinkles.

The first wrinkle is which layer closes the statistical loop. As the table above shows, LaunchDarkly calculates significance natively on the same flag data; Firebase Remote Config typically leans on Firebase A/B Testing or Google Analytics for that read; ConfigCat and Unleash hand the raw exposure signal to whatever analytics stack you already run. Before trusting a rollout percentage as a finished result, confirm which side of that line your own stack sits on, the same distinction our guide to implementing server-side A/B testing walks through for backend experiments generally.

The second wrinkle is specific to mobile: app version fragmentation. A rollout that spans devices on several different installed app versions at once is comparing more than the flag, because older builds may carry different bug fixes, different UI, or a different set of code paths entirely. The cleanest read comes from a flag rollout running inside a single, stable app version, once the app-store phased release for that version has already finished climbing, not while both percentages are moving at the same time. Our guide to progressive rollouts and canary releases covers the guardrail-metric discipline that keeps a staged expansion honest, on mobile or anywhere else.

Pitfalls that catch mobile teams specifically

Pitfall Why it happens Fix
App Store rejection over a hidden feature A flag reveals functionality that reviewers never saw, violating Guideline 2.3.1 Disclose the feature and a way to trigger it in the Notes for Review before submission, even if it launches disabled
Mistaking a flag for the code-download exception Guideline 2.5.2 bans downloading and executing new code; a flag that fetches new logic, not just a value, crosses that line Only ship behavior that is fully compiled into the reviewed binary; a flag should choose between paths, not fetch new ones
Reading results across app versions A rollout spans multiple installed binaries with different code, muddying the comparison Read results only within one stable app version, once its own store rollout has finished
No hardcoded default for a flag The app assumes the network call will always resolve before render Every flag needs a safe default value shipped in the binary itself, per the cache and offline logic above
Fetch throttling during QA Rapid manual testing hits provider throttling (Firebase’s minimum fetch interval, for example) and QA sees stale values Use a shortened fetch interval in development builds only, never in production, as most providers explicitly document
Orphaned flags left in old binaries A flag removed from the backend still gets evaluated by users stuck on an old app version that never updated Keep a documented sunset date, and confirm the default value in old binaries stays safe indefinitely

Automate this with Donnu

Donnu A/B is not a mobile SDK and does not evaluate flags on a device: it is a web experimentation engine, and it will not pretend otherwise. But the pain this guide walks through, rolling out safely, watching a guardrail, and only trusting a result once it is statistically real, is exactly the same pain your web team feels once a mobile flag rollout graduates from “ship it and watch” to “can we prove this actually helped.” If the surface your rollout touches includes a web page, a checkout flow, or a marketing site sitting alongside your app, Donnu gives you the calculated sample size, the honest bayesian read, and the isolated per-account data that a raw flag rollout percentage does not provide on its own. Start a 14-day free trial for the web half of that story, and pair it with whichever mobile flag tool from this guide already controls your app.

Read also: Feature Flags: The Complete Guide · Feature Flags vs A/B Testing · Progressive Rollouts and Canary Releases Explained · Feature Flag Tools Compared.

References

Frequently asked questions

Can a feature flag get my app rejected from the App Store?
A flag itself does not get an app rejected, but how you use it can. Apple's App Store Review Guidelines (2.3.1) require that all functionality be described with specificity in the Notes for Review and be accessible to reviewers, so a feature hidden behind a flag and switched on only after approval violates that rule unless Apple was told about it and given a way to review it first. Separately, Guideline 2.5.2 bans downloading and executing new code after approval, so a flag is only safe when it toggles a code path that already shipped inside the reviewed binary, not when it fetches new logic from your server.
What is the difference between an App Store staged rollout and a feature-flag rollout?
A staged rollout (Apple's phased release or Google Play's staged rollout) controls how many installed devices receive a new app binary, and it is tied to the app version: once a device updates, it keeps that code until the next update, and pulling back means shipping another release. A feature-flag rollout controls how many of those already-updated devices see a given behavior turned on, and it lives on your server, so you can raise it, lower it, or kill it in minutes, for every device already running that binary, without touching the app store at all.
Do I need Firebase Remote Config, LaunchDarkly, or ConfigCat if my app already uses phased release?
Usually yes, because they solve different problems. Phased release protects you from a broken binary reaching everyone at once, but once a build is out, you cannot change its behavior without a new submission and another review cycle. A remote flag tool lets you change behavior inside a binary that already passed review, instantly and without the app store in the loop, which is exactly what you need for a kill switch or a fast-moving rollout percentage.
What happens to a mobile feature flag when the device is offline?
It falls back to whatever was cached from the last successful fetch, or, if the app has never fetched successfully, to a default value hardcoded in the binary. This is why every mobile flag integration needs a safe, hardcoded default for each flag: Firebase Remote Config, LaunchDarkly, ConfigCat, and Unleash Edge clients all evaluate locally from cache first, and none of them can block the app waiting on a network call it may never complete.
Can I run a real A/B test using feature flags on a mobile app?
Yes, but the flag tool typically only handles the bucketing and the exposure event, not the statistical read. LaunchDarkly and, on the web side, tools like PostHog calculate significance natively on top of their own flag data, but plenty of mobile-first setups (Firebase Remote Config plus Google Analytics, or Unleash plus an external analytics tool) only emit the raw signal and expect a separate statistics engine to declare a winner. Confirm which side of that line your stack sits on before trusting a rollout percentage as if it were a finished experiment.