Case study · Payments

Hamrah Card

A mobile payments app serving five million people, refactored from MVC to MVVM-C with on-device card-number detection, and kept shipping through a real scramble when the team's Apple Developer account went down.

Role
iOS Developer
Platform
iOS · Flutter (contingency channel)
Stack
Swift · MVVM-C · TensorFlow · ONNX · iXGuard
Scale
5M+ users · 98.9% crash-free · Jun 2022 – Sep 2023
Overview

Five million users, no room to pause.

Hamrah Card is a mobile payments and money-transfer app used by more than five million people. It handles transfers across dozens of partner banks, bill payments, and airtime top-ups. I joined as iOS developer during a period of continuous releases on an already-live, high-traffic app, and worked through an MVC-to-MVVM-C architecture refactor, a backend redesign, on-device card-number detection, and, at one point, a genuine production emergency that had nothing to do with code.

The problem

The app was live and under constant release pressure, running on an aging MVC codebase coupled to a backend that was itself being redesigned at the same time. The job came down to business continuity: keep releasing, hold the crash-free rate steady or better, and bring loading times down, all while re-architecting the client and its backend integration underneath everything else.

Constraints

  • 5M+ users already on the app. Every release had to be safe at that scale; being correct in isolation wasn't enough.
  • A continuous release cadence. There was no freeze window to do the architecture work separately from feature delivery.
  • A backend redesign running in parallel. The client had to keep working against old and new backend surfaces during the transition.
  • Card-number detection had to run on-device, not by sending camera frames to a server, for a payments app handling card data.
  • App hardening wasn't optional. A payments app carrying financial data needed real anti-tamper and obfuscation measures in the shipped binary.
Architecture

A native app, a hardened release, and a fallback channel.

MVVM-C replaced MVC as the app's structure, and an on-device model replaced manual card entry. For one stretch, a second delivery channel stood in for the App Store entirely.

Hamrah Card architecture: a native iOS MVVM-C app with an on-device TensorFlow/ONNX card scanner and an iXGuard-hardened release build talks to a backend undergoing redesign, with a Flutter and WebView contingency channel used during an App Store distribution outage. CONTINGENCY Flutter + WebView iOS & Android, during outage IOS APP · NATIVE Views Feature screens ViewModels + Coordinators MVVM-C, replacing MVC Card Scanner TensorFlow + ONNX, on-device Obfuscated Release iXGuard-hardened binary BACKEND New Backend API REST, redesigned Bank Network Integrations Transfers across partner banks Legacy Backend Phased out during migration Reliability Telemetry Crash + performance profiling REST
The Flutter/WebView build wasn't a redesign: it was a second delivery channel to the same backend, built to survive a distribution outage the native app couldn't route around.

The refactor moved the app from MVC to MVVM-C feature by feature, alongside a backend that was being redesigned at the same time. The client had to keep working against both surfaces through the transition. Card-number entry runs through an on-device scanner built on TensorFlow and ONNX, reading a card number from the camera without sending frames anywhere. Every release build ships through iXGuard obfuscation and hardening before distribution.

Engineering decisions

Refactor in place, ship the whole time.

01

MVC to MVVM-C, incrementally

The app's architecture moved from MVC to MVVM-C feature by feature rather than as a single rewrite. Release cadence never stopped for it.

02

On-device card detection with TensorFlow and ONNX

Card-number capture runs entirely on-device through a TensorFlow/ONNX model, reading a card from the camera without a round trip to a server (the right default for anything touching card data).

03

Client and backend redesigned together

The backend integration was rebuilt alongside the client refactor instead of after it, so the new architecture was designed against the API it would actually run on.

04

iXGuard obfuscation on every release

Shipped binaries go through iXGuard hardening as a standard, required part of the release pipeline for a payments app.

05

A Flutter and WebView build, as a contingency channel

When the team's Apple Developer account was suspended, the whole app was rebuilt in Flutter with a WebView shell, reusing the Android delivery path to keep iOS users served through the same web experience, until native distribution came back.

Trade-offs

What business continuity actually cost.

Incremental MVC-to-MVVM-C over a rewrite

Continuous releases to 5M+ users never stopped during the migration.

The cost

Old and new architecture patterns coexisted for a real stretch of the project. That's more to hold in your head per change than a clean-room rewrite would have required.

On-device card detection over server-side OCR

Card data never has to leave the device just to be read off a camera frame.

The cost

Model accuracy and performance are bounded by what can run acceptably on-device, across a wide range of phones, rather than by server-side compute.

A Flutter/WebView fallback over waiting out the outage

iOS users stayed served through the entire Apple Developer account suspension. The product never went dark on the platform.

The cost

A full second implementation to build and then retire: real engineering time spent on a channel that existed only because the account was suspended, not because the product needed it.

Technical challenges

The one that wasn't a code problem.

An Apple Developer account outage, mid-release-cycle

The team's Apple Developer account was suspended, cutting off native iOS distribution entirely, with no timeline for resolution. The response was to build the entire app in Flutter with a WebView shell in a short window, reusing the Android delivery path so iOS users kept a working product while the account issue was resolved. The native app went out again through TestFlight once distribution came back.

Refactoring architecture against a backend that was also changing

MVC-to-MVVM-C migration and a backend redesign ran at the same time, which meant the client had to stay correct against two backend surfaces through the transition instead of one stable target.

On-device detection across real hardware variance

A TensorFlow/ONNX card-detection model has to perform acceptably across years of iPhone hardware still in active use among 5M+ users, old devices included.

Performance work at a scale where small percentages matter

Reducing loading times by 12% and improving overall performance by 20% required systematic profiling across an app already carrying millions of daily sessions, where a regression is felt immediately, not eventually.

Performance & reliability

Measured under real load.

98.9%
Crash-free rate
Reached through systematic profiling, debugging, and architectural fixes.
−12%
Loading time
From targeted performance optimization work across the app's busiest screens.
+20%
Overall performance
Alongside a 7% reduction in bug count from cross-functional refactoring of legacy code.
Security

Hardening a payments binary.

  • Reverse engineering the shipped app — answered by iXGuard obfuscation applied to every release build before distribution.
  • Card data exposure during capture — answered by keeping card-number detection entirely on-device via the TensorFlow/ONNX scanner; camera frames are never sent off the device to be read.
  • Distribution risk — the Apple Developer account outage was a real, non-technical single point of failure in the release pipeline; the Flutter/WebView contingency channel existed specifically so a platform-account issue couldn't take the product fully offline for iOS users.

Deeper backend-side security (transaction authorization, bank-network integration security, fraud controls) sat with the platform's backend and banking-network layer, outside this client's scope.

Engineering journal

Fifteen months, one real emergency.

2022 · Kickoff

Joined a live payments app at scale

Started as iOS developer on an app already serving 5M+ users, in a continuous-release production environment.

2022 · Refactor

MVC to MVVM-C begins

Started migrating the app's architecture incrementally, feature by feature, without pausing releases.

2022–2023 · Build

Backend redesign, in parallel

Rebuilt the client's backend integration alongside a backend redesign happening at the same time, keeping both old and new surfaces working through the transition.

2023 · Build

On-device card detection ships

Shipped the TensorFlow/ONNX card-number scanner, moving card capture off manual entry and off any server round trip.

2023 · Incident

The Apple Developer account goes down

With native iOS distribution cut off, the team built the whole app in Flutter with a WebView shell in a short window so iOS users stayed served. The native app shipped again via TestFlight once the account was restored.

2023 · Outcome

Performance and reliability targets hit

Crash-free rate reached 98.9%, loading times down 12%, overall performance up 20%, and bug count down 7% through the engagement.

Lessons learned

What a real outage teaches.

  • Not every production emergency is a bug. A distribution account going down can take a native app offline just as completely as a crash. The fix isn't always a code fix either.
  • A fallback channel is worth building before you need it. The Flutter/WebView path existed because it had to be built fast, under pressure, and it turned what could have been a full outage into a manageable inconvenience.
  • Refactor and redesign in parallel only works with real discipline. Migrating architecture against a backend that's also changing means never having a single stable target, a real cost worth naming plainly.
Outcome

Continuity, through everything.

Hamrah Card kept releasing continuously to more than five million users through an architecture refactor, a backend redesign, and a real distribution outage that had nothing to do with the app's code. Crash-free rate reached 98.9%, loading times dropped 12%, and overall performance improved 20%. When the Apple Developer account went down mid-cycle, the product stayed in front of users anyway.