MAIBank
Main iOS contributor · also contributed to the shared Protobuf API contract and the Java backend · Dec 2017 – May 2019
The official iOS + Android retail mobile banking app for Moldova Agroindbank (MAIB), Moldova's largest bank - PIN / biometric login, card management, P2P and card-to-card transfers, bill payments, and cardless ATM withdrawal, all over a binary Protobuf API with bank-grade device-side crypto.
MAIBank is the consumer mobile banking product for Moldova Agroindbank (MAIB), the country's largest bank, built at Red River Apps. It is not one app but a small platform: a native Swift iOS app, a native Java Android app, a Java backend the apps talk to, and a shared repository of Protobuf schemas that is the single source of truth for every request and response across all three. By 2018–2019 it had passed 500,000 installs.
For an end user - a MAIB retail customer - it is everyday self-service banking on the phone: log in with a 5-digit PIN or biometry, see balances across current, credit, and deposit accounts, browse a cards carousel and block or unblock a card instantly, move money by P2P (person-to-person), card-to-card, or account-to-account transfer, pay utility and provider bills, withdraw cash from an ATM without a card via a one-time code ("Cash-by-Code"), review a paged transaction history with a custom date-range calendar, and find the nearest branch or ATM on a clustered map. Sensitive actions are confirmed with an SMS one-time password.
I was the main iOS contributor, and also worked on the shared Protobuf contract and the Java backend, from December 2017 to May 2019. The iOS app is Swift on classic UIKit / MVC with storyboards, manual dependency injection through singleton managers, and a dedicated migration layer that moves legacy users onto the new biometric / PIN security model.
The defining platform decision is that the whole thing speaks binary Protocol Buffers over HTTP instead of JSON / REST. Every DTO is defined once in a shared .proto contract and code-generated per platform in CI - Swift for iOS, Java for Android, and Java for the backend - giving a compact, strongly-typed, versioned wire format (an uncommon choice for a 2018-era mobile bank). The backend itself is distributed rather than monolithic: a customer-facing API (SparkJava / embedded Jetty over raw JDBC), a separate Spring Boot admin console, and a standalone push-notification microservice, all sharing one SQL Server database and the same contract.
A notable piece is the iOS secure storage. The local database is an encrypted Realm whose 64-byte key is wrapped two independent ways in the Keychain - once behind biometry (invalidated automatically if the enrolled fingerprints or face change) and once behind the PIN, which is stretched through PBKDF2-HMAC-SHA512 at 200,000 rounds - so either factor can unlock the vault and both bind the secret to the device, paired with TrustKit certificate pinning on a custom URLSession delegate. A built-in demo mode intercepts at the networking layer to serve canned Protobuf responses from a seeded database, enabling App Store review and offline demos with no live server.
Architecture & engineering highlights
- Contract-first Protobuf across three platforms - a single shared .proto submodule is the source of truth for every DTO, and CI regenerates the Swift, Android-Java, and backend-Java models from it. Binary Protobuf over URLSession / Volley / SparkJava (no Alamofire, no Retrofit, no JSON) gives a compact, strongly-typed, versioned contract. Messages only, no gRPC services.
- Defense-in-depth secure storage on iOS - an encrypted Realm database whose random 64-byte key is wrapped independently by two factors in the Keychain: biometry (SecAccessControl with .biometryCurrentSet, so the key dies if enrolled biometrics change) and a PIN stretched through PBKDF2-HMAC-SHA512 at 200,000 rounds via applicationPassword. Either factor unlocks the vault; both bind secrets to the device.
- TrustKit certificate pinning - RSA-2048, enforced, with primary and backup SHA-256 pins on a custom URLSessionTaskDelegate.
- Built-in demo / mock backend on iOS - an isDemoMode flag intercepts cleanly at the URLSession layer and serves canned Protobuf responses from a seeded Realm, enabling App Store review and offline demos with no live server and without polluting the real networking path.
- Real-time card-transaction ingestion on the backend ("MMC") - a bespoke long-running TCP socket listener consumes a continuous pipe-delimited feed of card transactions (stream reader → line parser → validator → record saver, with failed-line replay) that drives instant "you spent X" push notifications - a custom stream processor built in-house.
- Multi-protocol core-banking integration - the backend's integration layer abstracts three transport styles behind interfaces at once: SOAP (cards, invoices, inbound webhook), mutual-TLS REST (the e-commerce / merchant gateway, via a file-based PKCS#12 client cert), and JSON REST (accounts, deposits, credits) - keeping the domain layer transport-agnostic.
- A defense-in-depth migration layer on iOS that carries legacy users onto the new biometric / PBKDF2 security model without forcing a re-registration.
- Runtime configuration on both apps - Firebase Remote Config feature flags plus in-app backend-URL switching across environments, which also doubled as the QA harness.
Tech stack
iOS (my primary work)
Android
Backend
Shared contract
All rights belong to their respective owners.