The first decision: native, shared or a mix
A native app is written separately for each platform, in Swift for iOS and Kotlin for Android. A cross-platform app shares most of its code. A middle route writes the business rules once and the screens natively. The right choice follows from the product, not from fashion.
| Approach | What it means | Usually fits |
|---|---|---|
| Two native apps | Two codebases, one in Swift and one in Kotlin | Apps built around device features, graphics or a platform's newest capabilities |
| One shared codebase | A framework such as Flutter or React Native, with native code where needed | Most business apps: accounts, forms, lists, maps, payments and content |
| Shared logic, native screens | Rules and data code written once, interfaces written per platform | Teams with strong native skills who want one set of business rules |
Choosing between frameworks has its own pages, Flutter and React Native, so we do not repeat that comparison here. The article on native or cross-platform covers the wider question.
When native Swift and Kotlin is the right call
Native code earns its cost when the platform itself is the product. Choose it when:
- The app depends on a deep device feature, such as a custom camera pipeline, augmented reality, background Bluetooth or health sensors.
- It needs widgets, watch apps, car integrations or other system surfaces that frameworks reach late.
- Smooth, graphics-heavy interaction is the main experience, as in a drawing tool or real-time video.
- You need a new operating system feature the week it ships.
The cost is real: two codebases mean two sets of skills and bugs, and constant effort to keep features in step. Limit it by sharing the design system, the API contract and one backlog. Modern native screens use SwiftUI on iOS and Jetpack Compose on Android, both declarative, so the two codebases often mirror each other's structure.
Device features and how they differ
List the device features the product needs before choosing a technology, then check how each behaves on both platforms. The same feature is rarely the same on iOS and Android.
- Notifications. Delivery, channels and user controls differ. Never build a flow that depends on a notification arriving at an exact moment.
- Background work. Both systems limit what an app can do off screen, and some Android manufacturers add battery restrictions of their own. Design sync that resumes, not constant running.
- Location. Precise and approximate location, and foreground and background access, are separate choices for the user. Background access needs a clear reason.
- Biometrics. Face and fingerprint unlock run through system prompts, and your app never sees the biometric data.
Ask only for the permissions a feature needs, at the moment of use, and say why. Both stores ask you to declare how the app uses data, so every permission you add is also a line in your privacy answers.
Store review and release planning
Open a developer account for the organisation on each store early, because verification takes time. Both stores review apps against written policies that change, so read the current ones before you design a feature, not in launch week.
- Privacy declarations. Apple's privacy details and Google Play's data safety section must match what the app and its third-party SDKs really collect.
- Accounts. If people can create an account in the app, both stores expect them to be able to delete it. Check the current rules.
- Payments. Digital goods and subscriptions generally go through each store's billing, and the rules vary by store and region. Settle this before you design a paywall.
- Listings. Prepare screenshots, descriptions and keywords in English and Arabic.
- Rollout. Use TestFlight and Google Play's testing tracks for beta builds, then a staged rollout, so a faulty release reaches few people first.
Review times vary and rejections happen, so leave a buffer around any fixed launch date.
Secure storage and authentication
Every project starts with a threat review of the core flows. For a mobile app the questions are what is stored on the phone, what travels over the network and who can sign in. The usual answers:
- Secure storage. Keep tokens and keys in the iOS Keychain and the Android Keystore, never in plain preferences or files, and store as little as you can on the device.
- Sign-in. Use the system browser or the platform's authentication session for single sign-on, not a web view inside the app. Offer passkeys or biometric unlock for returning users, and multi-factor authentication where the data justifies it.
- Sessions. Issue short-lived access tokens, rotate refresh tokens and let the server revoke a lost device.
- The server decides. Anything inside an app can be read or altered, so check permissions, prices and limits on the server and keep real secrets there.
A security review before launch gives a prioritised fix list, and our cybersecurity service includes testing mobile apps. The guide to secure app development goes deeper.
Updates and compatibility
People do not update at the same time, so several versions of your app will talk to your servers at once. Plan for that from the first release:
- Minimum OS version. Choose it from your audience's real devices, not the newest phones. Each extra old version costs testing time.
- New system releases. Each platform ships a major version about once a year. Test against the beta builds when they appear, so the app works the day the release reaches users.
- Store requirements. Both stores raise their minimum build requirements over time, so keep dependencies current rather than upgrading in a rush.
- Old app versions. Version your API, keep older versions working for a stated period, and build a way to ask users on an unsupported version to update.
- Local data and switches. Write migrations so an update never loses data stored on the device, and use feature flags to turn off a faulty feature without waiting for store review.
Testing on devices
Simulators and emulators are fast and useful every day, but they do not reproduce real cameras, Bluetooth, push delivery, biometrics, battery behaviour or the speed of a weak phone. Pick a small device set from your audience: an older, low-memory Android phone, a recent flagship, a small-screen iPhone, and the minimum supported and latest OS version on each platform.
Then script the cases that cause the most failures:
- Interruptions: an incoming call, switching apps, low battery, a permission revoked in settings.
- Poor networks: airplane mode mid-task, a slow connection, a drop during a payment.
- Accessibility settings and Arabic right-to-left, with large text, a screen reader and long mixed-direction strings.
Automate what repeats with unit tests, UI tests (XCUITest on iOS, Espresso on Android) and a pipeline that builds every change.
Monitoring after launch
A release is the start of the work. Before launch, set up:
- Crash reporting, with the symbol files kept for each build so reports stay readable.
- Performance signals: start-up time, slow or frozen frames and network failures, watched per release and per device group, alongside each store's own quality reports such as Android vitals.
- Alerts that reach a person when a new version raises the crash rate, and a rule for when to halt a staged rollout.
- Privacy by design: keep personal data out of logs and analytics events.
Read reviews and support messages as a signal too, and fold what you learn into the weekly plan.
How we plan a build for both platforms
We settle the native-or-shared question in scoping, with the feature and device list in front of us. A discovery sprint produces a clickable prototype, a technical approach and a threat model of the core flows, so the choice rests on evidence. After that you see working builds on real devices at the weekly demo, and a security review comes before launch. The work belongs to our mobile development service.