Comparison

Native vs cross-platform: do you need Swift and Kotlin?

This is the first real fork in a mobile project, and it is usually decided by whoever is most confident in the room rather than by the product. Native means two codebases written in each platform's own language. Cross-platform means one codebase producing both apps. We build cross-platform by default and native where the product requires it — here is where that line actually sits.

We ship both Native and Cross-platform Last reviewed
The short answer

Build cross-platform unless you have a specific reason not to: for the overwhelming majority of business apps it produces the same product for roughly half the build effort and one team instead of two. Go native when the app is built around a platform capability at the edge of what is exposed — sustained camera or audio processing, deep OS integration, complex background behaviour — or when a large in-house team already works that way.

When each one is the right call

Pick this if…

Go native

  • The product is built around camera, audio or sensor processing at scale
  • You need deep OS integration: complex widgets, extensions, background behaviour
  • A platform feature ships and you must adopt it the week it lands
  • You already have separate iOS and Android teams and the budget for both
  • Regulatory or enterprise requirements name the native toolchains specifically
Pick this if…

Go cross-platform

  • The app is screens, forms, lists, an API and a login — which most are
  • You want both stores from one codebase and one team
  • Budget or timeline rules out building and maintaining the same thing twice
  • Feature parity across platforms matters more than platform-specific polish
  • You want one place to fix a bug rather than two

The differences that actually matter

Native and Cross-platform compared across the factors that decide which one a project should use.
NativeCross-platform
CodebasesTwo — Swift for iOS, Kotlin for AndroidOne, producing both apps
Team you needTwo skill sets, and usually two peopleOne, plus knowledge of how the platforms differ
Build effortRoughly two builds of everything that is not backendOne build, with platform-specific work where it is warranted
Access to new OS featuresImmediate — the platform APIs are what you are writing againstUsually quick, occasionally needs a native module written for it
Heavy device workThe strongest case for native — nothing sits between you and the hardwareFine for normal use; the wrong tool for sustained processing
Feel and conventionsNative by constructionNative or near-native, depending on framework and care taken
Keeping platforms in stepA permanent coordination cost — two implementations of every featureParity by default; divergence is a deliberate choice
Fixing a bugTwice, and they can driftOnce
Hiring laterTwo pools, both specialistOne pool, and a larger one
Store submissionStandard on bothStandard on both — cross-platform apps are ordinary store apps

Neither column is a verdict. Every row is a trade-off, and which side of it you want depends on the project — which is what the sections below work through.

The myth worth killing

Cross-platform is not why an app feels slow

The case for native is usually made on performance, and it is the weakest part of the argument for most products. Modern cross-platform frameworks compile to native binaries and produce ordinary apps that the stores treat like any other — there is no browser, no wrapper and no web view in a React Native or Flutter build.

When an app genuinely feels slow, the cause is almost never the framework. It is images shipped at four times the size they render at, a list that rebuilds every row on every change, an API that takes two seconds and blocks the first screen, too much work on the main thread, and a cold start nobody measured. Every one of those is an engineering problem that follows you into Swift and Kotlin unchanged.

The place performance genuinely decides this is narrower and more specific than the argument usually admits: continuous camera or audio processing, real-time computer vision, heavy on-device inference, complex background sync, or anything that must hold sustained frame rates while doing significant work. Those are real, and for them we would recommend native without hesitation.

If you are being told to build twice, ask which specific operation requires it. If the answer is a category rather than an operation, it is a preference wearing a technical argument.

What building twice actually costs you

The expensive part of native is not the first version. It is every version after it.

Two codebases means every feature is specified once and implemented twice, by two people who have to agree on behaviour that the specification did not think to pin down. Every bug is found once and fixed twice. Every refactor happens twice, or happens once and leaves the other platform behind. Over a year of ordinary product work, the coordination overhead reliably outweighs the initial build difference, and it never goes away.

It also changes what your roadmap can look like. With two teams, shipping a feature to both platforms simultaneously requires them to move in step, which means the faster one waits. Teams respond by staggering releases, and then live with a permanent gap where iOS has something Android does not — which is a product decision that was made for you by an architecture choice.

None of this argues that native is wrong. It argues that the cost is ongoing rather than upfront, and that it should be weighed against the specific capability you are buying rather than against a general feeling that native is more serious.

The cases where we would tell you to go native

We build cross-platform for most clients, so it is worth being explicit about when we say the opposite.

The first is sustained device work. If the product is built on continuous camera capture, real-time audio processing, computer vision, or on-device model inference, you want to be as close to the hardware as possible with nothing in between. Cross-platform frameworks can call into native code for pieces of this, but if that native layer is most of your product, you have written a native app with an unnecessary layer on top.

The second is deep operating system integration. Complex home-screen widgets, share and action extensions, watch and TV targets, sophisticated background execution, and tight integration with platform services are all easier where they were designed to live. Simple versions of these are fine cross-platform; elaborate ones stop being fine.

The third is timing. If your product must adopt a brand-new platform capability the week it ships, native removes the wait for framework support entirely. This matters for a small number of products and almost no others.

The fourth is organisational rather than technical. If you already have established iOS and Android teams, forcing them onto a shared codebase is a change-management project with a technology decision attached, and it usually is not worth it.

The middle path most people miss

The choice is not actually binary, and the useful version of it is per-feature rather than per-app.

Both React Native and Flutter can call into native code. That means the ordinary ninety per cent of your app — screens, navigation, forms, lists, auth, payments, settings — is written once, and the one part that genuinely needs to be native is written natively and called from it. You pay the two-codebase cost only where it buys you something.

This is how we handle most projects that have a single demanding requirement inside an otherwise ordinary app. It keeps the surface area of duplicated work small and explicit, and it makes the decision reversible: if that native module turns out to be unnecessary, it can be removed without touching the rest of the product.

The trap to avoid is discovering this too late. If you suspect a native module is coming, design the boundary early — a clean interface between the shared app and the platform-specific piece — rather than letting native code leak through the middle of your codebase over several months.

Answered before you ask

Do cross-platform apps get rejected by the App Store?

No. Apps built in React Native or Flutter are ordinary native binaries and are reviewed on exactly the same terms as anything written in Swift. Both frameworks are used by very large, very visible apps on the store. Rejections happen for the reasons rejections always happen — missing privacy disclosures, unclear subscription terms, requesting permissions without explaining why, incomplete metadata, or an app that reviewers judge to be too thin to warrant a listing. Framework choice is not among them. The one adjacent thing worth knowing is that a review can be triggered again by native changes, which is why over-the-air update mechanisms are limited to what they are allowed to change.

Will a cross-platform app look wrong on iOS or Android?

Only if it is built without care, and the same is true of a native app built without care. React Native uses the platform's own components, so it inherits each platform's conventions by default. Flutter draws its own widgets, so matching platform conventions is a deliberate decision — one that Flutter supports well but that somebody has to make. The apps that read as obviously foreign are usually the ones where a single design was applied to both platforms without accounting for navigation patterns, system fonts, back-gesture behaviour and standard controls. That is a design discipline question rather than a framework limitation, and it is part of what we do during the prototype rather than something discovered at the end.

How much cheaper is cross-platform, really?

For an app of ordinary complexity the saving is substantial but not a clean halving, because the parts that do not duplicate — backend, design, testing on real devices, store setup and project management — do not shrink just because the client code is shared. What genuinely halves is the app-layer implementation, which is usually the largest single line in a mobile quote. The saving is larger over time than at launch, because ongoing feature work and bug fixing are where building twice keeps charging you. We quote the specific scope rather than a percentage, because the honest answer depends on how much of your product is app code versus everything around it.

Can I start cross-platform and go native later if I need to?

Yes, and it is a more reasonable path than it sounds, because the expensive knowledge transfers even though the code does not. By the time you would consider it, you have a working product, a validated data model, a stable API, a design system and real evidence about what users actually do — which is most of what makes a rewrite risky when you start from nothing. The rewrite is the app layer, done twice, and you would be doing it with the specification already answered. It is also rarely all-or-nothing: the more common outcome is moving one demanding feature into a native module and keeping the rest, which costs a fraction of a rewrite. What we would not recommend is planning to rewrite from day one, because it tends to produce an app built carelessly on the assumption it is temporary.