Native or cross-platform: the trade-offs our mobile deliveries taught us
"Native or cross-platform?" is a question almost every client building an app asks. Most answers online take a side: cross-platform wins, or native is the only serious choice.
We would rather not take a side. Our repositories hold real, delivered work on four lines — Swift, Objective-C, Kotlin and Dart — dozens of native and mobile repositories in total. Alongside them, Xcode projects and several Android and Flutter projects are verifiable locally, and several iOS apps are live on the App Store. We have paid for both routes with delivered work, so what follows is a decision framework rather than a preference.
Four language lines, and what each is for
Our own distribution is not random. Objective-C accounts for the largest share of repositories, Swift comes second, and Dart (Flutter) and Kotlin each hold a block. Each lines up with a kind of project.
- Projects that use device capabilities deeply go native. A 3D scanning app was written in native Swift because it drives the depth camera, haptic feedback and rendering directly; in camera-capture apps even permission checks live in the native layer. Cross-platform is not impossible here — the debugging cost eats the benefit.
- Content and display apps work either way. We have delivered device cataloguing in Dart as well as audio and video content products, and delivered comparable products natively. The choice here is about team and long-term plan, not technical quality.
- Legacy iOS work lives in Objective-C. A good share of existing iOS projects are ObjC, and continued evolution, second-round development and componentisation (we have done local component splitting with the CTMediator family) all happen on that line. "New projects use Swift" does not mean an ObjC project should be torn down.
Four questions that usually settle it
- How deep does the app go into device capabilities? Network, location and push alone: cross-platform is fine. Camera streams, depth data, low-level Bluetooth and real-time rendering: at some point you need the native layer. Practical method: list every system capability the app uses and count how many fall into the "cross-platform plugin quality varies" bucket.
- How many years will the app live? For a short-lived operational product, cross-platform's development speed is a real gain. For a product with a five-year horizon and a later maintainer, native's readability and toolchain stability are worth more. Flutter's release cycle is part of that long-term cost: framework upgrades bring migration work.
- How will the team be handed over? Will a successor be easy to hire — one iOS engineer plus one Android engineer, or a single cross-platform developer covering both? Plenty of clients choose cross-platform because the budget covers one person. That is a legitimate reason, but be clear about the price: a single point of dependency.
- What do the existing projects already use? Adding features to an ObjC project with new Swift modules is routine. Dropping a Flutter module into an existing native project means handling hybrid-stack routing and state management. Selection rarely starts from zero; it starts from what you already have.
How to tell real native work from a web shell
This is where clients lose the most money. Real native projects and wrapped web pages leave different traces, and each one is checkable at acceptance.
- Ask for project files, not an installer. Real native delivery includes a .xcodeproj or .xcworkspace (iOS) or a complete Gradle project (Android). A shell cannot produce them, or produces something structurally hollow.
- Read the dependency manifest. A real native project has a Podfile (iOS, CocoaPods) or a build.gradle dependency tree, where third-party libraries are visible. A shell's core dependency is usually an embedded browser engine.
- Spot-check the system capability code. Take camera permission: a real native project contains the permission-handling implementation, while a shell shows only the authorisation popup belonging to a web page.
- Look at the module structure. A real native project splits modules and components by business area, matching the requirements document. A shell is typically one thin project plus a directory of H5 resources.
Our own deliveries stand up to all four checks: Xcode projects open one by one, dependencies can be inspected, and permission code traces to a specific file and line. The same list is something a client can copy directly for acceptance.
The trade, not the winner
Native versus cross-platform is a trade of development efficiency against capability depth and long-term cost. There is no universally right answer, only mismatched ones. Handing a five-year, device-heavy product to a cross-platform build is a mismatch; building a three-month campaign as native apps on both platforms is the opposite mismatch. Run the four questions and most of the disagreement disappears on its own.
If you are stuck on the choice, send us your scenario and the list of device capabilities the app must use, and we will go through the four dimensions with you. See Native iOS & Android apps.
Tell us what you want to build.
What the product does, which platforms or devices are involved, and your timing. We reply within three working days, in English, with questions or a short written proposal.