Offline-first data layer
A local database the UI reads from, a write queue that survives a force quit, and explicit rules for when the same record changed in two places.
Offline sync specMobile engineering
We build iOS and Android apps for users on a bus, in a basement, or on a four-year-old handset — signed builds in your own store accounts, and a release pipeline your engineers can run.
A mobile build is two projects. The first is the app. The second is everything between a working binary and a phone in someone’s hand: developer accounts, signing identities, privacy declarations, review queues, and a target API level Google raises every August. The device is also hostile compared with a browser tab — the OS kills your process, throttles background work, and hands you a network that disappears in a lift. We build against the oldest handset you support first.
You cannot roll back a mobile release the way you roll back a web deploy. Once a version is installed it stays installed, and some users will run it for a year. So we sequence backwards from release: store accounts, signing and an installable build exist in week one, before most features do. AI agents carry the repetitive load — screen scaffolding, test flows, parity passes across two platforms — while a named engineer owns the architecture and reviews every merge.
| What is true of this work | What it forces | What we do about it |
|---|---|---|
| Installed versions never go away | APIs must answer old clients | Versioned endpoints and forced-upgrade path |
| Store review gates every release | Fixes wait days, not minutes | Staged rollout with crash-rate halt |
| The OS kills background processes | Work must resume after termination | Durable queue replayed on relaunch |
| Updates must be signed identically | Key custody decided before launch | Play App Signing, your accounts |
| Target API level rises yearly | Upgrade work recurs every year | Annual OS pass scheduled upfront |
A local database the UI reads from, a write queue that survives a force quit, and explicit rules for when the same record changed in two places.
Offline sync specCertificates, provisioning profiles and Android keystores held in your accounts, with CI signing each merge to TestFlight and Play’s internal track. Without Play App Signing, a lost key strands installed users.
Signed CI pipelineNotification delivery you can audit, universal and app links that open the right screen from a cold start, and background refresh built around what each OS actually permits.
Deep link route mapCamera pipelines, Bluetooth peripherals, biometrics, Keychain and Keystore storage, location and sensors. Where the cross-platform layer cannot reach the device, we write the Swift or Kotlin module.
Typed native modulePurchase and subscription flows with server-side receipt validation, restore paths and grace periods, and one source of truth for entitlement that your backend owns, not the storefront.
Entitlement validation servicePrivacy manifests, required-reason API declarations, data safety answers that match what the binary actually sends, permission strings, tracking consent, account deletion, and the annual target API bump.
Store compliance checklistWe fix the floor first: which OS versions and handsets you support, who owns the developer accounts, whether there is review history to inherit, and which privacy and payment rules apply.
You getDevice matrix and store readiness plan
Within fourteen days a real build reaches your real phones: one screen, real authentication, one API call, signed and installed through TestFlight and the Play internal track. The pipeline works before features exist.
You getInstallable build on your devices
Features land as vertical slices — screen, state, API, migration — and each goes to your testers the week it is written. Automated flows run the critical journeys on a device cloud nightly.
You getWeekly test builds and flow reports
We run the app on your worst supported device, throttled network, permissions denied, storage full — fixing cold start, memory and ANRs. Then listings, review notes and a staged rollout with a crash-rate halt.
You getApproved listings and release runbook
React Native, unless the app lives close to the hardware.
For list-and-form products backed by an API — booking, ordering, dashboards, workflow tools — one codebase on the New Architecture gets both platforms from one team, and users cannot tell. Two native codebases means two hiring pipelines and two release trains.
We’d choose otherwise whenyou need sustained frame rates, camera or Bluetooth pipelines, watch targets, or already run two platform teams.
Offline reading always; offline writing is a product decision, not a checkbox.
The moment two people can edit the same record on two phones, someone must decide which edit loses — and that is your call, not ours. We default to last-write-wins on a server clock with a visible conflict state.
We’d choose otherwise whenpeople genuinely co-edit the same record, which earns field-level merge and its extra testing.
Partly — treat over-the-air updates as infrastructure you own.
Over-the-air updates can replace the JavaScript layer of a React Native app, covering most bug fixes; they cannot touch native code, entitlements or the declared purpose. Since the hosted CodePush service retired, budget for a managed provider or your own update server.
We’d choose otherwise whenthe app is fully native, or your compliance regime requires every shipped binary to pass review.
If a wrapper would do, you probably do not need an app.
Both stores reject thin repackaged websites, and users judge a wrapper by its scroll. Ship a responsive web build instead and save the store overhead. Wrapping earns its place only when the native shell adds push, offline storage, biometrics, hardware or background sync.
We’d choose otherwise whenthe shell carries real native capability and the web views are a deliberate, temporary interior.
You do, completely. The app is published under your own Apple and Google accounts — store rules require the account holder to own the app — and we work inside them as invited members. Source, signing certificates, keystores, push keys and pipeline configuration are yours from day one and transfer in full at handover.
A signed build reaches your testers within 14 days of kickoff, through TestFlight and the Play internal track. Public launch depends on review as much as on us: plan two or three cycles for a first submission. A new personal Play account also needs twelve testers opted in for fourteen continuous days before production.
We fix it and resubmit at our cost; rejection is a normal part of a first launch, not a failure event. Most rejections concern declarations rather than code: a permission string that does not explain itself, a privacy answer contradicting the binary, a missing account-deletion path, or a reviewer who cannot get past your login.
Usually, after an audit we run before quoting. We need the repository, the signing assets, owner access to both store accounts, and an honest account of what still builds. The blockers we see are lost keystores, abandoned native modules with no maintainer, and a target API level below the store floor. You get a written verdict either way.
The recurring work is external, not internal: an annual OS release, a store target API deadline, an expiring distribution certificate, a push key rotation, a third-party SDK that stops being maintained. Defects we introduced are covered by a 30-day warranty. After that you either take the runbook and run releases yourself, or keep us on a retainer.
Thirty minutes with the engineers who would build it. You leave with a scope, a timeline and a fixed price — or an honest no, and the reason why.