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
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.
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.
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.
Refactor in place, ship the whole time.
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.
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).
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.
iXGuard obfuscation on every release
Shipped binaries go through iXGuard hardening as a standard, required part of the release pipeline for a payments app.
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.
What business continuity actually cost.
Incremental MVC-to-MVVM-C over a rewrite
Continuous releases to 5M+ users never stopped during the migration.
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.
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.
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.
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.
Measured under real load.
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.
Fifteen months, one real emergency.
Joined a live payments app at scale
Started as iOS developer on an app already serving 5M+ users, in a continuous-release production environment.
MVC to MVVM-C begins
Started migrating the app's architecture incrementally, feature by feature, without pausing releases.
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.
On-device card detection ships
Shipped the TensorFlow/ONNX card-number scanner, moving card capture off manual entry and off any server round trip.
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.
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.
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.
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.