Category: Mobile

  • Offline-First Architecture for Mobile Apps: A Practical Primer

    Offline-first gets requested frequently and implemented poorly. The usual pattern: an app ships, users in areas with patchy connectivity complain, and someone adds try/catch around API calls with a toast that says “You’re offline.” That’s not offline-first — it’s offline-aware. There’s a big difference.

    The core principle

    Offline-first means the app reads from and writes to a local store first, then syncs to the server in the background. The user never waits for a network response to see or interact with their data. Connectivity is treated as a nice-to-have optimisation, not a requirement.

    Choosing a local store

    For React Native, WatermelonDB is our default for relational data. It’s SQLite-backed, fast on large datasets, and designed specifically for offline-first sync patterns. For simpler apps (mostly read, occasional write), MMKV with a manual sync queue works with less setup.

    The sync queue pattern

    1. Write to local DB, assign a client-generated UUID
    2. Enqueue a sync operation
    3. When connectivity is available, flush the queue against the API
    4. On conflict, apply your resolution strategy (last-write-wins, merge, or user prompt)

    The conflict resolution strategy is where most implementations get lazy. Define it explicitly before you write a line of sync code — it’s almost impossible to retrofit.

    What to test

    Test the offline paths as first-class scenarios: airplane mode on device, background sync after reconnect, sync failure and retry. Most teams test happy path in CI and assume offline works. It rarely does until you test it deliberately.

  • Reducing Mobile App Cold Start Time: Lessons from a Fintech Project

    Cold start time is the metric most mobile teams ignore until a competitor’s app loads visibly faster in a side-by-side demo. For fintech apps where trust is paramount, a sluggish start signals instability before the user has seen a single number. When our client flagged a 4.2-second cold start on a mid-range Android device, we ran a structured optimisation sprint.

    Profiling first

    We used Android Studio’s CPU Profiler and the React Native startup logger to isolate where time was being spent. The breakdown was revealing:

    • JS bundle parsing: 1.8 s
    • Module initialisation (analytics, biometrics SDKs): 1.1 s
    • First render: 0.9 s
    • Other: 0.4 s

    Bundle parsing: the biggest win

    The bundle had grown to 4.1 MB uncompressed. We audited imports with react-native-bundle-visualizer and found three things: a full date library (moment.js, 280 KB) used for one format operation, a PDF viewer loaded eagerly but only used on one screen, and all Lottie animations included at startup.

    Switching to date-fns functions only, lazy-loading the PDF screen, and deferring Lottie imports reduced the bundle to 2.6 MB. Bundle parse time dropped to 1.0 s.

    SDK initialisation

    Analytics and crash reporting SDKs were initialising synchronously on the JS thread. Moving them to a deferred init pattern (fire after the first meaningful render) saved 0.7 s with no change in functionality.

    Result

    Cold start on the same device: 4.2 s → 1.4 s. The fix took four days of engineering time. Profile before you optimise — the bottleneck is almost never where you expect it.

  • React Native vs Flutter in 2025: What We Tell Clients

    Every few months a prospective client asks us to weigh in on React Native versus Flutter. Our answer has evolved as both frameworks have matured. In 2025 the technical gap has narrowed considerably — the decision is now more about team fit and ecosystem than raw performance.

    Where React Native wins

    If your team already writes TypeScript for a web product, the ramp-up on React Native is genuinely fast. Code sharing between web and mobile — especially business logic, API clients, and form validation — is practical and saves real time. The Expo ecosystem has matured to the point where you can ship to both stores without touching native code for the majority of app types.

    We reach for React Native when: the client has an existing React web team, the app is content-heavy or form-heavy, and there’s no specific need for complex custom UI with platform-native feel.

    Where Flutter wins

    Flutter’s widget system gives you pixel-identical UI across iOS, Android, and web from a single codebase. There’s no bridge overhead for animations, and the tooling — particularly hot reload and the DevTools profiler — is excellent.

    We reach for Flutter when: the design is custom and highly animated, performance on lower-end Android devices is a hard requirement, or the team doesn’t have existing React experience.

    The one thing that still tips the scales

    Hiring. In most European markets, React Native developers are significantly easier to find than Flutter/Dart developers. For clients who plan to hire a team post-handoff, that’s often the deciding factor.

    Neither choice is wrong. Both can produce excellent apps. The framework that the team shipping it knows best will produce the better product.