Mobile app development for Android and iOS
We build Android and iOS apps — React Native by default, native Swift or Kotlin where the app genuinely needs it. The hard parts of mobile are not the screens; they are what happens on a two-bar connection, on a three-year-old Android handset, and on the day App Review says no.
Who this is for
- You have a working web product and customers keep asking for an app, and you need someone to tell you honestly whether that means a real app or a better mobile site.
- Your app is in the stores but review rejections, crash reports on mid-range Android, or notifications that never arrive are eating your release cycle.
- You are shipping something that has to work where the network does not — field staff, delivery, retail floors, clinics, sites with no reliable signal.
- You need Android and iOS from one team, one codebase where that is sensible, and a release pipeline your own developers can run after handover.
What usually goes wrong
The problems this work exists to solve
It runs fine on the demo phone and stutters on the phone your users actually own.
We fix the baseline device first. Indian and emerging-market user bases usually skew to mid-range Android — modest RAM, slow storage, and thermal throttling after a few minutes of use. We ask for your actual device and OS distribution rather than assuming it. We agree that device as the performance target at kickoff and profile on it, not on a simulator. In React Native that means watching the JS thread separately from the UI thread — dropped frames during scroll are usually re-render storms, or work sitting on the JS thread that belongs on the UI thread — not a slow GPU. We virtualise long lists properly, move image decode and resize off the main path, memoise the components that actually re-render, and keep animations on the native driver so a busy JS thread cannot stall them.
On a bad connection the app spins forever, or the user taps twice and gets charged twice.
Requests that can change state go through a durable write queue on the device, not a bare fetch call. Each queued mutation carries a client-generated idempotency key, so a retry after a timeout is safely collapsed by the API instead of creating a second order or a second payment. We set real timeouts rather than relying on the OS default, back off on retry, and distinguish three states in the UI — pending, confirmed, and failed with a reason — because a spinner that never resolves is the most common way mobile apps lose trust.
App Review rejected the build and the launch date is gone.
We treat submission as an engineering task with a checklist, not a formality at the end. The recurring rejections are predictable: a thin wrapper around a website failing Apple's minimum functionality rule, permission purpose strings that describe the code instead of the user benefit, missing in-app account deletion, Sign in with Apple absent when third-party login is offered, and privacy declarations that contradict the SDKs actually linked in the binary. We resolve those before the first upload, ship early builds to TestFlight and the Play internal track so the review path is exercised weeks ahead, and budget calendar time for a rejection round rather than pretending it will not happen.
A one-line bug fix takes days because it is stuck behind a store release.
Mobile has no equivalent of pushing to production. We plan for that up front. Business rules that change often live server-side and are delivered as configuration, so behaviour can move without a binary. Risky features sit behind remote feature flags that can be turned off from a dashboard. Releases go out as staged rollouts on Play and phased releases on the App Store, so a crash spike in the first percentage of users halts the rollout instead of reaching everyone. Where the stack allows it, JavaScript-only fixes ship over the air, within the store rules on what may be updated that way.
Push notifications arrive for some users and silently vanish for others.
Push is best-effort transport, never a delivery guarantee, and we design around that. Aggressive OEM battery managers on Xiaomi, Oppo, Vivo and Samsung devices kill background processes and drop messages; APNs and FCM tokens rotate and go stale; silent pushes are throttled by iOS at the system's discretion. So the notification is treated as a hint, and the app reconciles real state on open. We refresh and de-register tokens properly, use high-priority data messages only where they are warranted, ask for the Android 13+ notification permission at a moment the user understands rather than on first launch, and instrument delivery so you can see the gap between sent and shown.
The app asks for a pile of permissions and installs drop off at that screen.
Every permission costs you conversion and adds a review risk. We map each one to the feature that needs it and remove the rest, request them in context rather than in a wall at startup, and design a working degraded path when the user says no — a location-dependent screen should still function with manual entry. Background location, precise location and full photo library access get particular scrutiny from both stores and are only requested where the product genuinely cannot work otherwise.
Scope
What you actually get
Android and iOS builds, live under your accounts
Signed release builds on the Play Console and App Store Connect, published from your own developer accounts. You keep the Android keystore, the Apple certificates and provisioning profiles, and the store listings. We document the signing setup so a later team is not locked out of your own app.
An offline and sync contract, written down
A per-entity decision on what is cached, how long it stays valid, what the user may create while offline, and how conflicts resolve when two devices edit the same record. This is a design document plus the sync layer that implements it, not a library dropped in and hoped for.
Push notification pipeline
Token lifecycle handling, APNs and FCM integration, notification categories and deep links that land the user on the right screen, quiet hours and opt-out handling, and a server-side send path with delivery instrumentation so failures are visible.
Release pipeline and staged rollout
CI builds on every merge, automated versioning and signing, distribution to TestFlight and the Play internal and closed tracks, and a documented rollout procedure with a defined halt condition. Your team can cut a release without us.
Crash, performance and adoption instrumentation
Crash reporting with de-obfuscated stack traces from real dSYMs and mapping files, startup and screen-render timings, and version adoption reporting — you need to know what share of users are still on an old build before you break an API.
Store readiness pack
Privacy declarations that match the SDKs actually in the binary, permission purpose strings, data safety and privacy label answers, account deletion flow, screenshots at required device sizes, and the age rating questionnaire completed.
Minimum-supported-version gate
A version check on launch with a forced-update screen, so you always have a floor under the oldest client you must keep supporting. Without it, one un-upgradable user base holds your API schema hostage indefinitely.
Technology
Technology options
We are technology-agnostic. These are the choices we reach for, and how the decision actually gets made.
Cross-platform
Our default, and the right answer for most apps: forms, lists, feeds, dashboards, bookings, commerce. One codebase, one team, one release cadence. The honest cost is dependency risk — a native module that goes unmaintained becomes your problem at the next OS upgrade — so we prefer well-supported libraries and keep the count of native dependencies low deliberately.
Native Android and iOS
The real cost of going native is not the first build, it is the second one: two codebases, two release trains, two sets of platform upgrades, and engineers who can staff both. Where a shared codebase is viable, that duplication is what it buys back. Where it is not — see the architecture note below for the specific triggers — the duplication is the price of the feature, and it should be a deliberate purchase rather than a discovery in month four.
On-device data and sync
Chosen by how much offline write capability you need. A read-mostly app is well served by a persisted query cache with sensible staleness rules. An app whose users create records offline for hours needs a real local database and an explicit sync engine. Picking the heavy option for a read-mostly app is a common and expensive mistake.
Delivery and release
Whatever runs unattended and produces an identical build from a clean checkout. We avoid signing from a developer's laptop — reproducible CI builds are what let you ship a hotfix on a day when the person who normally builds is unavailable.
Observability
Mobile crashes are reported after the fact by a percentage of users, so raw counts mislead. We track crash-free session rate by app version and by OS version, which is the number that tells you whether a rollout is safe to continue.
How it runs
The shape of the engagement
- 01
Baseline and constraints
We agree the target device floor, the minimum OS versions, the countries and network conditions, and whether offline write is in scope. These four answers determine most of the architecture, and changing them later is expensive.
- 02
Architecture and the API contract
We define the client-server boundary before UI work starts: which rules live on the server because they change, which state the device owns, how the API versions itself, and what happens when an old app version calls a newer API. If the backend is ours too, this is one conversation; if it is yours, we write the contract with your team.
- 03
Vertical slices, real builds every week
Each iteration ships one complete path — screen, local persistence, network call, error and offline states — installed on real devices through TestFlight and the Play internal track. You use the app on your own phone every week rather than reviewing screenshots.
- 04
Store readiness pass
Privacy declarations, permission strings, account deletion, deep links, screenshots and metadata are completed and a build is put through review well before launch, so the first submission is not also the first time anyone has read the guidelines.
- 05
Staged rollout
Release to a small percentage first, watch crash-free rate and the key funnel on the new version, then widen. A defined halt condition is agreed in advance so nobody has to make that call under pressure at 11pm.
- 06
Post-launch cadence
Mobile does not stop at launch: OS releases every year deprecate APIs, store policies change, and the long tail of old app versions has to be retired. We either run that cadence with you or hand it over documented, including the annual OS upgrade check.
Engineering
Architecture, security and performance
The decisions that are expensive to change later, and where we stand on them.
Architecture
- Cross-platform versus native is decided per app, not per company
- We do not have a house answer we sell to everyone. React Native is the documented default because most apps are UI over an API and one codebase halves the cost of every future change. But we will tell you to go native when the app's core is background execution, BLE, camera frame processing, on-device ML, complex gestures on custom canvases, or platform-exclusive surfaces. The decision is made in the first week with the reasons written down, because reversing it mid-build is one of the most expensive things that can happen to a mobile project.
- The app is a cache with a user interface
- Anything that might change — pricing rules, eligibility logic, limits, copy for regulated disclosures — belongs on the server, because you cannot force a client update on the day you need one. The app renders state and captures intent. This is the single decision that most determines whether you can respond to a problem in an hour or in a week.
- Offline is a per-entity decision, not a global switch
- 'Make it work offline' is not a requirement until you say which records are readable offline, which are creatable offline, and what happens when two devices disagree. Reference data can be cached aggressively with a version stamp. User-created records queue locally with idempotency keys and reconcile on reconnect. Records that must never be stale — a balance, a live slot availability — are better shown as unavailable offline than shown wrongly. Last-write-wins is a choice we make explicitly per entity, never a default we drift into.
- Your installed base is a long tail of versions, forever
- Unlike the web, you never have one version in production. Someone will be running a build from eighteen months ago. So the API is versioned and additive, breaking changes are staged behind a deprecation window, and a minimum-supported-version gate gives you a way to eventually cut off clients you can no longer support. We design the gate in on day one because retrofitting a forced-update screen requires the update you cannot force.
- Deep links and navigation state are part of the architecture
- A push notification, an email link and a QR code all need to open the right screen with the right state, including on a cold start when the user is not yet authenticated. We define the URL scheme, universal links and App Links, and the post-login redirect early — bolting this on later usually means reworking navigation across the whole app.
Security
- Nothing in the binary is secret
- An app package can be unpacked in minutes. API keys, third-party secrets and private endpoints compiled into the bundle are public information. Anything privileged is brokered by your backend and reaches the device only as a short-lived, scoped token. We audit the bundle for hardcoded credentials before release, and where a third-party SDK demands a key we check whether that key can be restricted by bundle identifier and platform.
- Token storage and biometrics on device
- Session and refresh tokens go in the iOS Keychain, and on Android into EncryptedSharedPreferences or an encrypted DataStore whose key is held in the Android Keystore — never in plain preferences or AsyncStorage. Refresh tokens rotate, and reuse of a rotated token invalidates the session. Where the app gates entry with Face ID, Touch ID or BiometricPrompt, that gate protects local access only — the server session stays authoritative, so a stolen unlocked device still cannot outlive a server-side revocation.
- Certificate pinning, with the trade-off stated
- Pinning defends against a compromised or user-installed trust root, which matters for payments and health data. It also means a certificate rotation you forgot about bricks every installed copy of your app until users update — and you cannot hotfix a pinned binary quickly. We pin only where the threat model justifies it, always with backup pins, a rotation calendar, and a server-controlled way to disable enforcement.
- What the device is allowed to keep
- In regulated work the offline cache is a data-residency and retention question, not just a performance one. We decide explicitly what may persist on device, encrypt the local database where the classification requires it, use the OS file protection classes correctly so data is unreadable while the device is locked, and wipe local stores on logout, on session revocation and on failed integrity checks.
- Screen and clipboard exposure
- For financial and clinical screens we handle the details that are easy to miss: excluding sensitive views from the app switcher snapshot, blocking screen capture with FLAG_SECURE on Android where policy demands it — iOS offers only after-the-fact detection, and we say so rather than promising parity, keeping account numbers and tokens off the shared clipboard, and turning off keyboard autofill and caching on fields that should not be learned by the OS.
Performance
- Cold start is a budget, and it is spent before your code runs
- Time to first meaningful screen is set by native init, JavaScript bundle parse, and whatever you insist on doing before first paint. We enable Hermes, keep the initial bundle small by splitting rarely used screens out of it, defer analytics and non-critical SDK initialisation past the first frame, and never block the first screen on a network call — cached content renders immediately and refreshes underneath.
- Battery is spent on radio wakeups, not on bytes
- Waking the cellular radio costs far more energy than the payload that travels over it, and it stays awake for seconds afterwards. So chatty polling is the most common battery bug we find. We batch requests, coalesce background work into the windows the OS already grants, and prefer a push-then-delta-sync pattern over an interval poll. On Android we work with Doze and App Standby rather than fighting them, and use a foreground service only when the user can see why it exists.
- Lists and images decide how the app feels
- Almost all perceived jank in a content app is in scrolling. We use windowed lists with stable keys and fixed item heights where possible, request server-side resized images rather than downscaling full-resolution files on device, cache decoded bitmaps with a memory bound, and keep gesture-driven animation on the UI thread so it survives a busy JS thread.
- Optimistic UI, backed by a real queue
- The app should acknowledge a tap immediately and reconcile afterwards, which is what makes a slow connection feel usable. That is only safe when the write queue is durable across app restarts, idempotent on retry, and honest in the UI when something eventually fails. Optimistic updates without that backing produce the worst outcome in mobile: a user who believes something was saved when it was not.
- Measured on the floor, not the ceiling
- Performance work is validated on the agreed baseline device on a throttled connection, and on a device that has been running the app for a while — not on a freshly launched app on a flagship on office wifi. Startup time, frame drops during the main scroll, and memory growth over a long session are the numbers we track across releases.
Size of work
Where your project probably sits
A rough map so you can locate yourself before talking to us. Actual scope comes from a conversation, not a table.
| Level | Example | Engagement shape |
|---|---|---|
| Focused task | Adding push notifications with deep links to an existing app, getting a rejected build through App Review, setting up Fastlane and a signed CI release pipeline, or fixing scroll performance on Android. | One engineer, days to a couple of weeks, ending with a signed build on TestFlight or an internal-track APK installed on your own device. From about Rs 25,000. |
| A single-purpose app | A field data capture app for staff, a booking or ordering app on top of an existing API, or an internal tool distributed through managed enrolment rather than the public stores. | A few weeks with a small team, weekly builds on your devices from the first iteration, one store submission cycle included in the plan. |
| A consumer or operations product | A multi-role app with authentication, payments, offline record creation with sync, notifications, deep links, and both stores live with staged rollouts. | Several months, a team of engineers plus design, running a release train with a fixed cadence rather than one big launch. |
| Platform with a long-term release train | Apps for several user types sharing a codebase, running alongside the backend and the web app, with an annual OS-upgrade cycle, version deprecation and a supported-version policy. | Ongoing engagement with a dedicated team, quarterly roadmap, and defined ownership of the release calendar and the store accounts. |
Honest limits
When this is not what you need
Telling you this early is cheaper for both of us than discovering it in month two.
- If a responsive web app or an installable PWA would serve your users, we will say so and point you at web development instead. An app you have to persuade people to install, keep updated and grant permissions to is a real ongoing cost — it should buy you something specific: hardware access, reliable offline use, push, or a home-screen habit.
- If the plan is to wrap an existing website in a WebView so you can say you have an app. Apple's minimum functionality rule exists precisely for this and rejects it, and even when a wrapper gets through it usually underperforms the mobile site it copies.
- Games and heavy real-time 3D. Unity, Unreal, custom render pipelines and game economies are a different discipline, and you would be better served by a studio that does only that.
- If you need an app in the stores next week for an event. Store review, signing setup and account verification alone can consume that window, and a build rushed through it is a build you will be fixing publicly.
Related work
Projects in this space
Fintech payment platform
A payments platform built with a tokenisation boundary that keeps card data out of the application's scope — the same constraint that decides what a mobile client is allowed to hold on device.
Read the case study: Fintech payment platformHealthtech patient system
Clinical record integration work, where what may be cached locally and for how long is a compliance decision before it is a performance one.
Read the case study: Healthtech patient systemHeadless commerce migration
A storefront rebuilt against a decoupled commerce API — the API shape that lets a web front end and a mobile client be served by the same backend.
Read the case study: Headless commerce migration
Questions
Common questions
How long does app store review take, and who owns the accounts?
Review itself is typically fast once your account is established, but the honest planning assumption is that a first submission may come back with a rejection, so we budget a round for it. The accounts should be yours from the start — your Apple Developer account, your Play Console, your keystore and certificates. We work inside them and hand over the signing material documented. If your app is under a developer account we control, you do not really own your app.
Do you build the backend the app talks to?
We do, and it is usually cleaner when the same team designs both, because the client-server boundary is where most mobile problems originate. That work is covered on our API development page. If you already have a backend and a team behind it, we build against it and write the contract with them — including API versioning and how old app versions are supported, which is a mobile-specific constraint backend teams often have not had to plan for.
How much does a mobile app cost?
It depends on the number of user roles, whether offline write and sync are in scope, whether payments are involved, and whether one codebase can serve both platforms. Small bounded mobile work — adding notifications, fixing a rejected submission, setting up a release pipeline — starts around Rs 25,000. A full consumer or operations app is a project-scale engagement. We scope it in writing before you commit, and tell you which band you are in on the first call.
Keep reading
Related work and reading
Related services
- API Development
The app is a client. If the API is chatty, unversioned or lacks idempotent writes, no amount of mobile engineering will make the app feel fast or safe on a weak connection.
- Web Development
Read this if you are still deciding whether you need an app at all — for many products a fast responsive web app reaches more people at a fraction of the ongoing cost.
- MVP Development
If the app is the first version of a new product, the scoping problem comes before the platform problem — this covers how we cut a build down to the loop worth proving.
- Software Testing
Device fragmentation is what makes mobile QA different: this covers device matrix coverage, automated UI runs on real hardware, and pre-release regression on the versions still in the wild.
- Software Maintenance
Apps do not stand still — annual OS releases deprecate APIs and store policies change. This covers the ongoing cadence that keeps an app shippable year after year.
Tell us what the app has to do when the signal drops
Send us what you are building, who uses it and on what devices, and whether it has to work offline. We will come back with a straight recommendation on React Native versus native, what the store submission path looks like, and a scoped plan — including telling you if a web app would serve you better.