perfectdesign.

Mobile platforms

Cross-platform app development

One codebase for Android and iOS is our default, and it is a real saving. It is not the saving most proposals imply, because the two stores, the two sets of rules and the two sets of device tests do not merge.

In short

Cross-platform React Native is our default for mobile, so one codebase serves Android and iOS, and we write native Kotlin or Swift when a feature genuinely needs it. What a shared implementation saves is the duplicated product work: one set of business rules, one design system, one backlog, one team, features and fixes that land on both platforms together. What it does not remove is two store accounts, two release processes, two sets of platform rules with their own deadlines, and testing that has to be done separately on each side. Decide it feature by feature rather than as a slogan.

Written for a business owner or product lead deciding whether one codebase can serve both android and ios · 8 min read

A shared codebase still needs platform decisions

Cross-platform React Native is our default. One codebase serves Android and iOS, and we use native Kotlin or Swift when a feature genuinely needs it. We build in React by preference on web and mobile, which is why that default exists, and we work in other languages when a project calls for it. Other frameworks make a similar trade, and the reasoning on this page applies to them as well.

Four decisions settle whether a shared implementation fits your project: which parts of the product can share one implementation, where platform-specific code or a native module is required, how releases and regressions are tested separately on each platform, and who owns framework and dependency upgrades afterwards. None of those are answered by choosing a framework. They are answered feature by feature, against the recurring task, the interruptions and connectivity your users work in, and the staff or backend workflow behind the app.

If you have not yet decided whether an installed app is the right surface at all, mobile app development covers that first. This page assumes the answer was yes and that both platforms are in scope.

Malaysia is a two-platform market. Ship to one platform in Malaysia and you have written off roughly two in five of the people you wanted. The split is close enough that the question is which comes first, not which one. A full text version follows.

Malaysia is a two-platform market

Published statistic. Ship to one platform in Malaysia and you have written off roughly two in five of the people you wanted. The split is close enough that the question is which comes first, not which one.

Source: StatCounter GlobalStats: mobile OS share, Malaysia, Sept 2025 to Aug 2026. Reviewed .

Read the graphic as text

Malaysia

  • Android: 60.15%. The larger share, and the wider range of devices to test on
  • iOS: 39.58%. Nearly two in five, and higher in the cities where most buying happens
  • Everything else: 0.27%. Samsung Internet on other systems, and a long tail
Download this infographic (SVG)

What one codebase genuinely saves

The saving is real and it is worth being precise about where it comes from. You write the business rules once: how a booking moves through its states, what a role may do, how prices and validation work, how the app talks to your systems. You design one system of screens and components rather than two. You run one backlog and one team, so a change is specified, built, reviewed and tested once. Features arrive on both platforms at the same time instead of one lagging behind, and a fix ships to both without being reimplemented and retested twice.

For most business apps, that shared portion is the bulk of the work: lists and forms, records and states, authentication, sync, staff workflows and reporting. The visible platform differences sit at the edges, around prompts, navigation conventions, device capabilities and release mechanics. That is the honest case for the approach, and it holds up well for the kind of operational and customer apps we are usually asked to scope.

Your launch date is not entirely yours. Plan the submission weeks before the launch campaign, not after it. The twelve-tester rule has delayed more first releases than any code problem. A full text version follows.

Your launch date is not entirely yours

Published statistic. Plan the submission weeks before the launch campaign, not after it. The twelve-tester rule has delayed more first releases than any code problem.

Source: Apple: App Review. Reviewed .

Also: Google Play Console Help: publish your app.

Also: Google Play Console Help: testing requirements for new personal accounts.

Read the graphic as text
  • Apple review: 24h. Apple says 90% of submissions are reviewed in under a day
  • Google extended review: 7d. Some accounts get a longer review, up to seven days or more
  • New Play accounts: 12 × 14. A new personal Google Play account must run a closed test with 12 testers opted in for 14 continuous days before it can publish
Download this infographic (SVG)

Which features can safely share an implementation?

Classify each requested feature into one of four buckets before anyone picks a framework version: shared, where one implementation serves both platforms; adapted, where the logic is shared but the interface follows each platform's conventions; native, where the capability needs platform code behind a shared interface; and conditional, where the feature exists on one platform only and the other needs an agreed fallback. Inspect the workflow steps, the state transitions and the controls separately, because a step can be shared while the control that triggers it is not.

Map the workflow before choosing the framework

The table below works one fictional booking app through the classification. It is an invented example: the roles, policies and responses are assumptions, and the last column describes tests to run before sign-off rather than results already achieved.

A fictional booking app assessed for Android and iOS, one row per requested capability
Workflow stepAssumed actorProposed system responseExceptionAcceptance evidence to collect
Choose a bookingFictional customerFollow a shared booking flow with platform-appropriate controlsA platform capability gap needs an explicit native or web fallbackTask and capability comparisons recorded separately on Android and iOS
Request a device permissionFictional app userShow the platform prompt at the moment the feature needs itDenial leaves a usable agreed fallback rather than a dead controlPer-platform permission and denial-path observations, including the route back through settings
Buy an optional extraFictional customerOffer the extra through the payment route each store expects for what is being soldThe stores treat digital and physical purchases differently, so one platform may need its own handlingA written check of the current rules in each store for the item being sold, made before the flow is built
Prepare a releaseFictional release ownerTest the chosen framework and plugin versions and build separate store packagesA failure on one platform prevents claiming both are readySeparate build, device and release-check records for each platform

Turn your own feature list into rows

Do the same for every feature you want. Write the shared hypothesis, the platform exception you expect, and the evidence that would settle it. Connect each row back to the recurring task and to the staff or backend workflow that supports it, because a customer-facing feature usually implies an operational one. The selected framework version, the plugins and the actual responsibilities stay open until requirements and compatibility have been reviewed, and pretending otherwise is how a scope acquires surprises.

Sort every feature into one of four buckets. A workflow step can be shared while the control that triggers it is not. A full text version follows.

Sort every feature into one of four buckets

Editorial framework. A workflow step can be shared while the control that triggers it is not.

Basis: React Native: Platform-specific code. Reviewed .

Read the graphic as text
  • Shared. One implementation serves both platforms
  • Adapted. Same logic, controls follow each platform
  • Native. Platform code behind a shared interface
  • Conditional. One platform only, with an agreed fallback
Download this infographic (SVG)

Where you still write platform-specific code

Platform-specific code is a designed part of the approach, not a failure of it. React Native's documentation, read on 18 September 2026, describes the mechanisms plainly: a platform module exposing which operating system the app is running on, a select helper that returns a different value per platform, file extensions so that a component can have an Android version and an iOS version loaded automatically, and a version property for checking the operating-system version. Those exist because the documentation expects differences, and using them is ordinary work rather than a workaround.

A capability with no shared route needs a native module. React Native's documentation on native modules, read the same day, sets out the shape: a typed specification, generated interfaces, then native code written in Kotlin or Java on Android and in Objective-C or Swift on iOS. That means the work is done twice and tested twice, so a native module is a scope item to name in advance, not a detail. It is also usually cheaper than abandoning the shared codebase for the other ninety per cent of the product.

There are projects where we would not recommend a shared implementation at all: sustained high-performance graphics, deep integration with platform-specific hardware or system services, or a product whose entire value is one platform capability. If that is your app, we will say so rather than sell the approach we prefer. Neutral comparisons of native against cross-platform, and of one framework against another, are subjects in their own right and go deeper than a service page usefully can.

Platform interface conventions are a requirement, not an afterthought

Sharing an implementation does not mean the two apps should feel identical. Permission prompts, navigation patterns, back behaviour, system controls and the states shown when a capability is unavailable all follow platform conventions, and users notice when they do not. Treat those as requirements with acceptance evidence: collect the observations separately on Android and iOS, including what each platform shows when a permission is refused, what the agreed fallback looks like, and whether the task can still be completed through it.

An app is not finished when it ships. This is the honest reason an app carries a care fee. Leave it a year untouched and the store quietly stops offering it to new phones. A full text version follows.

An app is not finished when it ships

Published statistic. This is the honest reason an app carries a care fee. Leave it a year untouched and the store quietly stops offering it to new phones.

Source: Google Play Console Help: target API level requirements. Reviewed .

Read the graphic as text
  • New apps and updates: API 36. Must target Android 16 from 31 August 2026
  • Existing apps: API 35. Below Android 15 and the app stops reaching new users on newer devices
  • How often: Yearly. Google moves the floor every year, and Apple moves its own alongside each iOS release
Download this infographic (SVG)

Testing and releasing two apps from one codebase

Plan for two sets of evidence. Build a matrix organised by critical workflow, platform, device tier, operating-system condition, permission state, connectivity condition and recovery path, then choose the devices from your audience and the capabilities you asked for rather than from a catalogue. Record the roles, permissions, normal paths, exception paths, integrations and acceptance criteria as evidence to collect at sign-off. For this kind of project we recommend a simple rule: a failure on one platform blocks the combined readiness decision until it is resolved or the agreed scope changes.

The release side is where the two platforms diverge most. Each store has its own account, its own packaging, its own review and its own moving requirements. Read on 18 September 2026, Google's documentation states that from 31 August 2026 new apps and updates must target Android 16, API level 36, or higher to be submitted to Google Play, while Apple's submission guidance states that from April 2027 iPhone and iPad apps must be built with the iOS and iPadOS 27 SDK or later. Those two clocks are unrelated to each other and neither is affected by your codebase being shared. Plan the release as two workstreams that happen to compile from one source.

Who owns framework and dependency upgrades?

This is the question most proposals skip, and the one that decides what the app costs to own. React Native's upgrade guidance, read on 18 September 2026, is candid about why: a project is effectively an Android project, an iOS project and a JavaScript project together, so moving between versions can touch generated native files as well as dependencies, which is why the documentation provides a helper showing the full set of changes between any two versions. Every third-party plugin carries its own native code and its own upgrade timing, and one that lags behind can hold a release.

So write the ownership down. Who controls the store accounts and signing material. Who approves an upgrade and who runs the compatibility testing. What happens when an upgrade fails, and who decides whether to roll back or hold the release. And where the line sits between routine maintenance, a new feature and a larger upgrade, because each deserves a different approval path. We maintain apps after launch, covering operating-system and device compatibility updates, crash monitoring and fixes, dependency and framework upgrades, and resubmission when an update ships. It is scoped per project and quoted with the work.

One upgrade, four projects to keep in step. Name who approves an upgrade and who runs the testing before one becomes urgent. A full text version follows.

One upgrade, four projects to keep in step

Primary-source guidance. Name who approves an upgrade and who runs the testing before one becomes urgent.

Source: React Native: Upgrading to new versions. Reviewed .

Read the graphic as text

One framework upgrade

  • Android project. Gradle build files, Kotlin or Java code
  • iOS project. Xcode build files, Swift or Objective-C code
  • JavaScript side. Dependencies moving on their own clocks
  • Every plugin. Native code again, on its own release timing
Download this infographic (SVG)

What to bring to a first conversation

Bring the recurring workflow, who performs it and on which devices, the systems it must connect to, the capabilities you believe the app needs, and any constraint or date you are working to. From that we can tell you which parts genuinely share an implementation, which need platform-specific work, where a native module is likely, and whether one platform first is the more sensible sequence for your users.

On evidence we would rather be plain than impressive. Nothing we have built is published on the App Store or Google Play. Our own PantryCue app is the closest answer: React Native, with live camera scanning, local notifications, an offline pantry and a Cook Mode that reads steps aloud. You can try the web build, and there is an installable Android build. Alongside it, the HORIZON 2026 concept adds an attendee app and a door check-in console that run as web applications, and more app samples are being built. Aruva Marketplace is a concept prototype too, and it shows the multi-role shape that usually sits behind an app of this kind. Bring the problem to a scoped enquiry when you are ready.

What you get

What is actually delivered

01

A feature classification you can argue with

Every requested feature sorted into shared, adapted, native or conditional, with the platform exception you expect and the evidence that would settle it.

02

One shared implementation of the rules

Business logic, states, roles, validation and integrations written once, so a change is specified, built and tested once rather than twice.

03

Platform-appropriate interfaces

Prompts, navigation, back behaviour and unavailable-capability states that follow each platform convention, rather than one layout forced onto both.

04

Native modules, named in advance

Where a capability has no shared route, the platform code behind a shared interface, scoped as its own item because it is written and tested twice.

05

Separate test evidence per platform

A matrix by workflow, platform, device tier, operating-system and permission state, with connectivity and recovery paths recorded on each side independently.

06

Two release packages, one source

Store accounts in your name, signing and provisioning prepared, review builds distributed and each package submitted on your behalf.

07

An upgrade and ownership plan

Who approves framework and plugin upgrades, who tests compatibility, what happens when one fails, and where maintenance ends and new work begins.

08

Handover that stands on its own

Source code in your own repository, accounts and keys in your business name, and the test and release records agreed in the scope.

How it runs

Decide it feature by feature

Cross-platform is a set of per-feature decisions, not a purchase. We make those decisions in the open, including the ones that go against the approach we prefer.

  1. 01

    Classify the features

    We sort your feature list into shared, adapted, native and conditional work, and name the platform exceptions before they become surprises in a build.

  2. 02

    Confirm capabilities and systems

    We check which capabilities each platform actually supports for your use, and confirm that the business systems behind them have an interface, an access owner and terms that allow it.

  3. 03

    Prototype you can click

    You get the main journey and the staff side on a live link, so the scope is judged from something real before the build is committed.

  4. 04

    Build shared, adapt where needed

    We build the shared implementation in React Native, adapt the interface to each platform convention, and write native code only where a capability requires it.

  5. 05

    Test and release both sides

    Separate device and regression evidence per platform, then two store packages prepared and submitted from one source, with accounts and code handed to you.

  6. 06

    Maintain both platforms

    After launch we track operating-system, framework and plugin upgrades, monitor crashes, run compatibility testing and resubmit when an update ships.

Proof

Work you can click through

PantryCue is our own React Native app, with live camera scanning, local notifications, an offline pantry and a Cook Mode that reads steps aloud. You can try the web build, and there is an installable Android build; it is not on the App Store or Google Play. HORIZON 2026 is a concept prototype, not client work. Its attendee app and door check-in console show the roles, states and staff-side effects an operational app needs, and both run as web applications.

Concept prototypes are labelled as prototypes everywhere they appear. They demonstrate what we can build, not work delivered for that named client. More client work is going live and will be added as it does.

How we work

The parts people ask about before they commit

How we build

React first, other languages when a project needs them

We build in React by preference, on both web and mobile, and we work in other languages when a project genuinely calls for it.

App stores

The accounts stay in your name

Your business owns the Apple Developer and Google Play accounts. We set them up in your name, prepare certificates and signing, and submit builds for you.

  • Apple Developer and Google Play account setup in your name
  • Certificates, signing and provisioning
  • TestFlight and internal test tracks for review builds
  • Store listing preparation and submission on your behalf

Timeline

Project dependent, and often quicker than expected

Timelines are project dependent. A focused build can go live in about a week, while a larger platform takes longer once scope is agreed.

Ongoing care

Quoted with the project, not bolted on

For a more complex website or a system with a real backend, ongoing care starts from RM 250 a month, with the plan confirmed against what was actually launched. Care is quoted with the project, not bolted on afterwards.

Getting hold of us

Normally under one working day

We normally respond to a support request in under one working day, and we work to solve problems as fast as we can. That is how we normally work rather than a contractual guarantee, and responding is not the same as resolving. If your operation needs a formal response or resolution commitment, we can write one into your scope.

Ownership

Everything belongs to your business

You own everything we build for you: the code, the content, the domain, the hosting account and every third-party account opened for the project. There is no lock-in. If you move to another provider, everything goes with you and we help with the handover.

  • Source code, handed over in your own repository
  • Domain and DNS, registered to your business
  • Hosting and every service account, in your name
  • Analytics, search and ad accounts, with us as a manager you can remove
  • All content, media and data in the system

Questions

Asked about cross-platform apps

Straight answers to what people ask before they commit. Anything else, message us.

Is a cross-platform app worse than a native one?

For the operational and customer apps we are usually asked to scope, no. The shared portion is lists, forms, records, states, authentication, sync and staff workflows, and a shared implementation handles those well. The approach struggles when the product is essentially one platform capability, sustained high-performance graphics, or deep integration with platform-specific hardware. If that describes your app, we will say so.

Does one codebase halve the cost?

No, and we would rather set that expectation early. It removes the duplicated product work: one set of business rules, one design system, one backlog, one round of specification and review. It does not remove two store accounts, two submission processes, platform-specific interface work, native modules where a capability needs one, or testing on each platform separately. An app is quoted against its scope rather than sold as a package.

Which framework do you use?

React Native is our default, because we build in React by preference on both web and mobile, and it lets one codebase serve Android and iOS. We use native Kotlin or Swift when a feature genuinely needs it, and we work in other languages when a project calls for it. The framework and plugin versions are confirmed against your requirements rather than picked in advance.

Can we launch on one platform first?

Yes, and it is often sensible when your users are clearly on one side. The shared codebase means the second launch is mostly release work rather than a rebuild, but it is not free: that platform still needs its own device testing, its own store account and submission, and its own interface review. Treat it as a smaller second project rather than a switch to flip.

Can we see a cross-platform app you have published?

No. Nothing we have built is published on the App Store or Google Play, and we would rather say that than imply otherwise. Our own PantryCue app is the closest answer: React Native, with live camera scanning, local notifications, an offline pantry and a Cook Mode that reads steps aloud. You can try the web build, and there is an installable Android build. Alongside it, the HORIZON 2026 concept adds an attendee app and a door check-in console that run as web applications. More app samples are being built and will be added here.

Who owns the code and the store accounts?

You do. The source code is handed over in your own repository, and the Apple Developer and Google Play accounts are set up in your business name. We prepare the certificates and signing, run review builds through TestFlight and internal test tracks, and submit on your behalf. If you move to another provider, everything goes with you.

What happens when the framework or a plugin needs upgrading?

It gets planned rather than avoided. Upgrades can touch the native Android and iOS parts of the project as well as the JavaScript dependencies, and a third-party plugin that lags behind can hold a release, so compatibility testing is part of the work. We cover framework and dependency upgrades, operating-system compatibility, crash monitoring and resubmission under ongoing care, scoped per project.

Where to go next

Sources

Tell us what you need built

We will show you the closest thing we have already built, then scope the real version against your requirements.