← Our services Mobile applications · technically

Native is a behaviour, not a framework.

Mobile engineering is choosing what must belong to the device, what can be shared, and what has to keep working through interruption. We use Swift and Kotlin, React Native, or a governed WebView shell according to the product — then engineer lifecycle, permissions, offline state, device capabilities, accessibility and store delivery as first-class contracts.

The implementation map

What the app has to survive.

These are mobile responsibilities before they are framework choices. The tools show where our work has landed; each linked section explains the engineering contract behind them.

LayerWhat we use
Platform & application shapeSwift / SwiftUIKotlin / ComposeReact Native / ExpoHybrid WebView
Application architectureUnidirectional stateMVVMKotlin MultiplatformPlatform adapters
Native SDK & module engineeringKotlin / SwiftReact Native modulesCocoaPodsGradleTyped events
Navigation & lifecycleNative stacksExpo RouterDeep linksState restoration
Device capabilitiesBLECameraLocationMotionPushBackground services
Offline data & syncSQLite / SQLDelightLocal cacheWorkManagerConflict policy
Hybrid WebView contractsTyped bridgeOrigin policyHealth watchdogsNative handoff
Media & performanceNative playbackResumable uploadImage pipelinesPerformance budgets
Testing & accessibilityUnit testsSensor simulationUI automationDevice QAVoiceOver / TalkBack
Release & operationsEAS BuildFastlaneSDK distributionStore rolloutDiagnostics
Platform & application shape

Choose what must be native, not how much code can be shared.

The first mobile decision is where the product needs platform fidelity, device access and independent release control. We choose Swift and SwiftUI, Kotlin and Jetpack Compose, React Native with Expo, or an explicit hybrid shell from those requirements. Code reuse matters, but it follows the interaction, hardware, lifecycle, performance and team constraints rather than overruling them.

Fully native when
  • The product depends deeply on Bluetooth, sensors, media, background execution or platform frameworks SwiftKotlin
  • Platform-specific interaction and performance are central to the experience SwiftUIJetpack Compose
  • iOS and Android need independent capability or release roadmaps XcodeGradle
React Native when
  • A shared product surface is valuable but navigation and device behaviour must remain native React NativeExpo
  • The required device capabilities have maintained modules or a deliberately owned native module Expo modulesNative modules
  • One team should ship both platforms without pretending platform differences do not exist TypeScriptPlatform files
Hybrid WebView when
  • The primary product already lives on the web and needs a focused native host WKWebViewAndroid WebView
  • Native capabilities can be expressed as a small, versioned bridge contract Typed messagesCapability adapters
  • The shell owns navigation, permissions, recovery and external handoff—not merely a URL Native shellWeb runtime

We can work inside other serious mobile stacks. These are current choices, not the edge of our capability.

Application architecture

Share the rules, keep the platform seams visible.

A mobile codebase has to remain understandable through view recreation, process death, intermittent connectivity and years of SDK changes. We separate presentation state, domain rules, data access and platform capabilities, then decide which layer can be shared. Native UI can sit over common Kotlin domain and persistence code; React Native can isolate Expo and native APIs behind typed adapters; a hybrid shell can keep its bridge contract independent of its screens.

  • One observable state model drives each screen and makes loading, empty, error and recovered states explicit Unidirectional stateMVVM
  • Domain behaviour stays independent of view controllers, composables and React components Use casesState reducers
  • Network, storage, clock and device services enter through replaceable interfaces RepositoriesPlatform adapters
  • Cross-platform logic is shared only where both platforms have the same contract Kotlin MultiplatformTypeScript
  • Design tokens can be shared while touch targets, typography and controls respect each platform TokensNative components
  • White-label, environment and product variants share modules without sharing mutable configuration Expo configGradle flavorsXcode schemes

A shared codebase should reduce duplicate decisions. It should not hide platform behaviour behind conditionals spread through the UI.

Native SDK & module engineering

Make the native boundary a product of its own.

A cross-platform SDK is a compatibility surface across several runtimes: the underlying iOS and Android frameworks, the Swift and Kotlin adapters, the React Native bridge, the JavaScript or TypeScript API, and every consumer build. We design that surface deliberately, preserve platform differences where they matter, and release it with the documentation, sample applications, testing hooks and version evidence expected of a maintained product.

The public contract
  • Typed configuration, results, errors and discriminated events map consistently across platforms TypeScriptKotlinSwift
  • Asynchronous native operations resolve explicit success and failure instead of leaking bridge timing PromisesOperation results
  • Events that arrive without a foreground UI are persisted before being exposed to product state Event emitterDurable handoff
  • Platform-only capabilities remain marked and testable rather than forced into false parity Capability queryPlatform API
  • Native dependencies integrate through standard package surfaces CocoaPodsGradleNPM
A releasable SDK includes
  • A compatibility matrix for native SDK, React Native, Xcode, CocoaPods and Android build versions
  • Semantic release notes that identify additive, deprecated and breaking behaviour
  • Minimal iOS and Android sample applications for integration and regression checks
  • Generated API documentation and migration guides built from the published types
  • Test-only native hooks that cannot be enabled accidentally in production

A native module is not finished when it compiles in the example app. It is finished when a consumer can integrate, upgrade, diagnose and test it without reading its internals.

Device capabilities

Make hardware access a product contract.

Bluetooth, camera, location, motion, notifications, biometrics and background work are not library checkboxes. Each capability has permissions, unavailable states, operating-system limits and privacy consequences. We isolate it behind a narrow interface, request access in context, explain the value before the system prompt, and design the useful path when access is denied or hardware disappears.

  • BLE discovery, connection, GATT services and packet handling kept behind a device state machine Core BluetoothAndroid BLE
  • Camera, image selection and media capture with bounded file and privacy handling AVFoundationCameraXExpo ImagePicker
  • Location, motion and geofencing modes selected from the actual foreground or background need Core LocationFused LocationActivity Recognition
  • Push registration separated from notification routing and user preference state APNsFCMExpo Notifications
  • Credentials and sensitive local material kept in platform-protected storage KeychainKeystoreBiometrics
  • Background work scheduled within power, network and operating-system constraints BGTaskSchedulerWorkManager
  • Continuous sensing recovers after process loss, device restart and application replacement Foreground serviceBoot receiverBackground session
  • Battery, permission and sensor restrictions become actionable settings health DiagnosticsSettings handoff
For every permission
  • The request follows a user action and a plain-language rationale
  • Denied, restricted and permanently denied states have distinct UI
  • The application remains useful when the capability is absent
  • Collection and retention are no broader than the feature requires
  • Always-on access has an explicit consent, pause, deletion and support path

The operating system owns the final permission decision. The product owns whether that decision is understandable and recoverable.

Offline data & sync

Design the product for the missing network.

Offline is not a banner shown after a failed request. It is a decision about which data is authoritative, which actions can be queued, how freshness is communicated, and what happens when two devices change the same record. We keep a bounded local model, record pending operations durably, make retries safe, and reconcile against the server without silently discarding user intent.

Local data model
  • Structured product state persisted with explicit schema and migrations SQLiteSQLDelight
  • Media and derived caches separated from user-authored or authoritative state File cacheEviction policy
  • Sensitive records encrypted or deliberately excluded from local persistence Encrypted storageRetention
Sync protocol
  • Queued mutations have stable identifiers and can be retried safely
  • Freshness and pending state are visible to the user
  • Conflict behaviour is chosen per domain, not hidden behind last-write-wins
  • Background sync respects power, metered network and OS scheduling limits
  • Local schema and cached data survive application upgrades

A cache accelerates a read. An offline model preserves a user workflow. They require different guarantees.

Hybrid WebView contracts

Make the bridge smaller than the app.

A serious hybrid application is a native host with a governed web runtime—not a full-screen browser. We define the bridge as a versioned API, keep native capabilities narrow, permit only trusted origins, and supervise both document loading and web-app readiness. Navigation, external URLs, process termination, slow starts and bridge incompatibility all receive explicit policy and recovery UI.

  • Typed request, response and event schemas shared by native and web consumers Bridge contractRuntime validation
  • First-party origin allowlists and separate policy for frames, external links and development Origin policyNavigation guard
  • Document-load, hydration-readiness and liveness signals monitored independently WatchdogsHealth probes
  • Hardware back, deep links and push destinations translated into product routes Route bridgeHistory contract
  • Media, browser, telephone and other specialised flows handed to native surfaces Native playbackSystem browser
  • Content-process eviction and renderer failure recover without trapping the user Reload policyBounded retry
Bridge release gate
  • Old native shells can tolerate additive web changes
  • Unsupported bridge versions fail visibly before a destructive action
  • Every native method validates origin, payload and permission state
  • A browser-based test bed can exercise the web half without a device

A WebView reduces duplicated interface code only if the bridge, lifecycle and failure states are engineered as first-class mobile behaviour.

Media & performance

Budget the device, not only the response time.

Mobile performance includes launch time, frame stability, memory pressure, battery, radio use and package size. We measure on representative physical devices, keep image and list work away from the critical interaction path, stream large media, and move playback or capture into native components when the web or JavaScript runtime cannot meet lifecycle and resource requirements.

  • Cold and warm launch paths measured through the first usable interaction Launch tracingReadiness markers
  • Long lists virtualised and recomposition or re-render boundaries kept narrow Lazy listsMemoisation
  • Images resized, cached and decoded for the rendered target rather than the source asset Image pipelineResponsive assets
  • Video streamed and handed to platform playback where backgrounding or controls require it AVPlayerExoPlayerExpo Video
  • Large uploads resumable, observable and decoupled from one foreground session Multipart uploadProgress state
  • CPU, network and sensor polling constrained to protect battery and thermals ProfilingBackoff

A smooth simulator is not a performance result. Low-memory devices, slow storage, poor networks and background transitions are part of the benchmark.

Testing & accessibility

Test the contracts across real device states.

The fastest mobile tests hold domain rules, route parsing, bridge policies and sync decisions outside the UI. Integration tests then prove storage, permissions, native modules and network adapters; a smaller UI suite protects critical journeys on supported OS versions and devices. Accessibility is part of those contracts, including semantics, focus order, dynamic type, contrast, reduced motion and touch targets.

Verification layers
  • Pure state transitions, policies and parsers run without a simulator Kotlin testXCTestNode test
  • Storage, background workers, BLE and network adapters tested at their platform boundary InstrumentationKtor mock
  • Location, motion, trip and collision scenarios simulated deterministically—including while backgrounded Native test SDKPreset simulation
  • Compose, SwiftUI and React Native components checked in meaningful states Compose UIXCUITestReact Native tests
  • Critical launch, login, deep-link and offline journeys run on a maintained device matrix Device labsE2E
  • Hybrid web content receives browser tests and a native contract suite Test bedBridge tests
Accessible by construction
  • Every control exposes a name, role, value and useful focus order
  • Dynamic type and font scaling do not hide actions or content
  • VoiceOver and TalkBack complete the primary workflows
  • Animations respect reduced-motion preferences
  • Touch targets, contrast and error messages meet platform guidance

A screenshot-perfect UI can still be unusable with a screen reader, large text, one hand, a hardware keyboard or an interrupted network.

Release & operations

Treat the store build as an operated system.

Mobile delivery combines source, native dependencies, environment configuration, signing identity, store metadata and server compatibility. We automate reproducible builds, separate product and environment variants, distribute betas early, stage production rollout and connect crashes and performance regressions to the exact release. Because rollback is constrained by store review, compatibility and remote controls are designed before launch.

  • Deterministic iOS and Android builds from reviewed configuration EAS BuildXcodeGradle
  • Signing credentials and provisioning isolated from source and scoped to release automation App Store ConnectPlay signing
  • Native libraries and cross-platform modules published with compatible artifacts and types CocoaPodsGradleNPMSemantic versioning
  • Beta lanes publish to internal testers before store submission TestFlightFirebase DistributionFastlane
  • Staged rollout, feature flags and server compatibility reduce blast radius Phased releaseRemote config
  • Crashes, hangs, launch failures and performance tied to version and device context Crash reportingRelease health
  • Support can collect bounded on-device logs and state without exposing unrestricted user data Rolling logsDiagnostic bundle
  • Minimum-version and data-migration policy handles clients that cannot update immediately Version gatesBackward compatibility
Before expanding rollout
  • Automated tests and supported-device smoke tests are green
  • Privacy declarations, permissions and store metadata match runtime behaviour
  • The backend remains compatible with the previous supported app version
  • Crash-free sessions, launch health and critical conversion are within thresholds
  • Feature disablement and incident ownership are known

On mobile, “roll back” often means waiting for another reviewed binary. Safe releases depend on compatibility, staged exposure and remote containment.

Where this comes from

We have built at every mobile seam.

We have engineered the mobile seams that fail outside the happy path: BLE and sensor state, background location, durable local queues, typed native bridges, deep-link routing and media handoff. Those systems recover after process death, restart and app upgrades, expose permission and battery restrictions, and reproduce device events for testing.

What we have built
How we work

An AI-native team, with a factory behind it.

Team Foundry is our software factory — what the team uses day in, day out to deliver projects. It accelerates the work and validates it: every change arrives with the checks it passed, the session that produced it, and a person accountable for it.

How Team Foundry works
Factory

Let's find the work AI should be doing in your organization.

Whether you are exploring an idea or improving a system already in use, we’ll help you decide what is worth doing next.

Prefer to start async?

hello@thoughtfulrobots.ai

Hyderabad · Remote

Questions before you book?

Read the FAQ

Follow along

START A CONVERSATION

Tell us about the work.

A few lines about your product and where AI might belong. We usually reply within one business day.