Skip to main content

Mobile Architecture Document

Mobile Architecture Document

Project: Drop — Fintech Payment App Version: 0.1.0 Date: 2026-02-23 Author: John (AI Director, ALAI) Status: In Review Reviewers: Alem Bašić (CEO)

Document History

Version Date Author Changes
0.1 2026-02-23 John Initial draft — from ADR-011, MOBILE-APP.md, source code analysis

1. Framework & Rationale

Framework Pros Cons Decision
React Native (Expo SDK 54) JS/TS codebase reuse with Next.js web app, large ecosystem, OTA updates via EAS, managed workflow Bridge overhead, larger binary (~25MB), Expo-compatible packages only Selected
Flutter High performance, consistent UI, strong typing Dart language (no code sharing with React web app), no native OTA Rejected
Swift (iOS native) Best iOS performance, latest APIs iOS only, separate codebase Rejected
Kotlin (Android native) Best Android performance, Material 3 Android only, separate codebase Rejected

Selected: Expo SDK 54 (managed workflow) Version: Expo SDK 54, React Native latest, Expo Router v4 ADR Reference: docs/architecture/adr/ADR-011-expo-mobile-framework.md

Rationale: Drop's web app uses React 19 + Next.js. Expo enables sharing React components, hooks, types, and business logic between web and mobile — reducing duplication and bugs. BankID authentication requires opening a secure browser (expo-web-browser) and handling deep link callbacks (drop://auth/callback), which Expo provides natively. QR scanning is a core Drop feature requiring camera access — expo-camera provides built-in barcode scanning. OTA updates via EAS enable rapid hotfixes for financial apps without App Store review cycles. The AI-driven development team benefits from a single language (TypeScript) across all platforms.

Runtime environment:

  • Min iOS: iOS 16
  • Min Android: Android 10 (API 29)
  • Target SDK: Android 34 / iOS 18

2. Project Structure

src/drop-mobile/
├── App.js                          # Expo entry (unused — Expo Router takes over)
├── app/
│   ├── _layout.js                  # Root Stack layout + font loading + splash screen
│   ├── index.js                    # Welcome screen (green bg, "drop." wordmark)
│   ├── login.js                    # Login screen (email + password)
│   ├── register.js                 # Registration (2-step: info, password)
│   ├── history.js                  # Transaction history (filter tabs, FlatList)
│   └── (tabs)/
│       ├── _layout.js              # Tab navigator (4 tabs)
│       ├── index.js                # Dashboard / Home (balance card, transactions)
│       ├── send.js                 # Send money (2-step: recipient + amount)
│       ├── scan.js                 # QR scanner (camera + payment flow)
│       └── profile.js              # Profile & settings (recipients, logout)
├── lib/
│   ├── api.js                      # API client (Bearer token auth)
│   └── theme.js                    # Theme constants (colors, fonts, spacing, radius)
└── assets/                         # Static assets (images, icons)

Key design decision: Expo Router is file-based routing — the directory structure maps directly to navigation routes.


3. Navigation Architecture

graph TD
    Root["Root Stack Navigator\n(app/_layout.js)"] --> Welcome["Welcome Screen\napp/index.js"]
    Root --> Login["Login Screen\napp/login.js"]
    Root --> Register["Register Screen\napp/register.js"]
    Root --> Tabs["Tab Navigator\napp/(tabs)/_layout.js"]
    Root --> History["History Screen\napp/history.js (modal)"]

    Tabs --> Home["Home Tab\napp/(tabs)/index.js\n(Dashboard)"]
    Tabs --> Send["Send Tab\napp/(tabs)/send.js\n(Send Money)"]
    Tabs --> Scan["Scan Tab\napp/(tabs)/scan.js\n(QR Scanner)"]
    Tabs --> Profile["Profile Tab\napp/(tabs)/profile.js"]

Navigation library: Expo Router v4 (file-based, built on React Navigation)

  • URL scheme: drop://
  • BankID callback: drop://auth/callback?code=&state=
  • Library: expo-linking
  • Config: app.config.tsscheme: "drop"

Tab Navigator Configuration (4 tabs):

Tab Label Icon Screen
index Hjem Unicode house emoji Dashboard
send Send Unicode arrow emoji Send Money
scan QR Unicode QR emoji QR Scanner
profile Profil Unicode person emoji Profile

Tab bar style: backgroundColor: #FFFFFF, borderTopColor: #E5E7EB, height: 60 Active tint: #0B6E35 (Forest Green), Inactive: #9CA3AF

Note: Mobile has 4 tabs. Web has 5 tabs (adds "Aktivitet/Kontoer"). Mobile shows bank balance on dashboard, not a separate screen.

Auth flow: On login success, token stored → navigate to /(tabs). On logout → clear token → navigate to /.


4. Platform-Specific Considerations

Concern iOS Android Solution
Back gesture Swipe from edge Back button + gesture Expo Router handles both
Status bar Overlaps content Separate space SafeAreaView from expo
Permissions model Request at use time Request at use time + Manifest expo-camera permissions
Push notifications APNs FCM expo-notifications (unified)
Keyboard behavior Push up content May or may not push KeyboardAvoidingView
Font rendering System fonts crisp Sub-pixel differences Custom font loading via expo-google-fonts
Haptics UIFeedbackGenerator Vibrator API expo-haptics (future)
Secure storage Keychain Keystore expo-secure-store
BankID browser In-app secure browser In-app secure browser expo-web-browser
Camera / QR AVFoundation Camera2 API expo-camera

5. Build Variants & Flavors

Variant Bundle ID API URL Debug Analytics Push Env
Dev no.getdrop.app.dev http://localhost:3000/api Yes Off Development
Staging no.getdrop.app.staging https://drop-app-staging.vercel.app/api No Off Development
Production no.getdrop.app https://drop-app.vercel.app/api No Yes Production

Environment variable handling: expo-constants via app.config.ts

API URL Detection (current implementation):

// lib/api.js
const API_URL = __DEV__
  ? "http://localhost:3000/api"
  : "https://drop-app.vercel.app/api";

6. Code Sharing Strategy

Shared with web app (src/drop-app/):

  • TypeScript interfaces (User, BankAccount, Transaction)
  • Business logic (age validation, XSS input sanitization)
  • API endpoint paths
  • Brand tokens (colors, spacing) — same values in theme.js and globals.css

Mobile-specific:

  • React Native StyleSheet (vs Tailwind CSS on web)
  • lib/api.js — Bearer token auth (vs httpOnly cookies on web)
  • lib/theme.js — Native theme constants
  • Expo-specific modules (expo-camera, expo-web-browser, etc.)
  • 4-tab navigation (vs 5-tab on web)

Web-only:

  • Next.js App Router
  • shadcn/ui components
  • Server Components
  • httpOnly cookie auth
  • Cards feature (feature-flagged on web, not present on mobile)
  • Merchant dashboard (on web, not on mobile)

7. Screens Detail

Welcome (app/index.js)

  • Full-screen green background (#0B6E35)
  • "drop." wordmark in white Fraunces 700 font
  • Headline: "Enklere betalinger. Lavere gebyrer."
  • Two buttons: "Logg inn" (outline) → /login, "Opprett konto" (solid white) → /register

Login (app/login.js)

  • White background
  • Email + password fields with React Native TextInput
  • "Logg inn" green button → api.login(email, password)
  • On success: stores token in AsyncStorage, navigates to /(tabs)
  • "Opprett konto" link → /register
  • Pre-filled demo credentials in __DEV__ mode

Register (app/register.js)

  • 2-step flow with progress dots indicator (vs 4-step on web)
  • Step 1: firstName, lastName, email, phone
  • Step 2: password, confirmPassword
  • api.register(data) → on success navigate to /(tabs)

Dashboard (app/(tabs)/index.js)

  • Greeting: "Hei, {firstName}" with hand wave emoji
  • Green balance card: "Total saldo" — formatted NOK amount
  • Quick stats row: Sendt, Mottatt, Ventende (sent, received, pending counts)
  • "Siste transaksjoner" section with FlatList
  • Each transaction: direction icon (arrow up=sent, arrow down=received), name, date, amount with color coding
  • "Se alle" link → /history
  • Pull-to-refresh via RefreshControl

Send Money (app/(tabs)/send.js)

  • 2-step flow (vs 4-step on web — simpler mobile UX)
  • Step 1: Recipient name input + currency picker (5 currencies with flags)
    • Currencies: BAM (Bosnia), RSD (Serbia), PKR (Pakistan), TRY (Turkey), PLN (Poland)
  • Step 2: Amount input (NOK) + conversion card showing exchange rate, 0.5% fee, "Mottaker får" calculated amount
  • "Bekreft og send" → api.sendRemittance(data) → success screen

QR Scanner (app/(tabs)/scan.js)

  • Camera placeholder (gray box with QR icon) — simulated in current version
  • "Skann QR-kode" instruction text
  • "Simuler skanning" button for demo
  • Nearby merchants list (hardcoded: Ahmetov Kebab, Kafe Oslo, Narvesen)
  • Payment flow: merchant info → amount input → confirm → success
  • api.payQR({ merchantId, amount })

Transaction History (app/history.js)

  • Filter tabs: Alle, Sendinger (remittance), QR (qr_payment)
  • FlatList with transaction items (direction icon, name, date, amount)
  • Pull-to-refresh
  • Filter changes trigger re-fetch via api.getTransactions({ type })

Profile (app/(tabs)/profile.js)

  • User info section: initials avatar, name, email
  • "Mine mottakere" section with recipients list (fetched from api.getRecipients())
  • Each recipient: name, country, account number
  • Settings menu: Sprak, Varsler, Personvern, Vilkar
  • Logout button → clears token, navigates to /index

8. Native Module Integration

Module Purpose Drop Feature
expo-camera Camera access + barcode scanning QR payment scanning
expo-web-browser Secure in-app browser BankID OIDC authentication
expo-notifications Push notification handling Transaction alerts, payment receipts
expo-linking Deep link handling (drop://) BankID callback, notification deep links
@react-native-async-storage/async-storage Persistent key-value store Bearer token storage
expo-secure-store Encrypted storage (Keychain/Keystore) Sensitive data (future biometric)
expo-local-authentication Biometric auth (Face ID/fingerprint) App unlock — Phase 2
expo-haptics Haptic feedback Payment confirmation — Phase 2
expo-google-fonts Font loading Fraunces, DM Sans

New native module process:

  1. Check if Expo SDK covers the need
  2. Check community modules (well-maintained, TypeScript types)
  3. Custom native module only as last resort
  4. Any native module must have TypeScript wrapper

9. Performance Optimization Strategy

Strategy Implementation Status
JS thread optimization Minimal computation — no heavy processing in current version Done
Image caching No images in current version — text-based UI N/A
List performance FlatList with keyExtractor Done
Bundle size Hermes engine enabled (Expo default) Done
Font loading expo-google-fonts — loads async, SplashScreen prevents rendering until ready Done
Memory management useEffect cleanup in API calls Partial
Render optimization No React.memo yet — simple screens don't need it Planned

Performance targets:

  • App cold start to interactive: < 2 seconds
  • Screen transition: < 300ms (React Navigation default)
  • List scroll: 60fps
  • Bundle size: < 25MB (Expo managed workflow)

10. CI/CD for Mobile

flowchart LR
    PR["Pull Request"] --> UnitTests["Unit Tests\n(Jest)"]
    UnitTests --> Build["Build\n(EAS Build)"]
    Build --> Distribute["Distribute\n(TestFlight / Firebase App Distribution)"]
    Distribute --> QA["QA Approval"]
    QA --> Store["Store Submission\n(EAS Submit)"]
Stage Tool Trigger
Build EAS Build Push to main or release/*
OTA hotfix EAS Update Any JS-only change to production
Test distribution TestFlight (iOS) / Firebase App Distribution (Android) Every staging build
App Store submit EAS Submit Manual trigger (release manager)
Code signing Expo managed credentials Automated

EAS advantage: OTA updates allow hotfixes to JavaScript bundle without App Store review. Critical for financial apps.


11. Architecture Diagram

graph TB
    subgraph "Mobile App (Expo SDK 54)"
        Screens["Screens (7 screens)\nWelcome, Login, Register, Dashboard,\nSend, Scan, Profile, History"]
        Nav["Expo Router v4\nStack + Tabs navigation"]
        State["Local State (useState)\nNo global state library"]
        APIClient["api.js — API Client\nBearer token auth"]
        AsyncStorage["AsyncStorage\nToken persistence"]
        SecureStore["expo-secure-store\nFuture: sensitive data"]
    end

    subgraph "Expo Native Modules"
        Camera["expo-camera\nQR scanning"]
        WebBrowser["expo-web-browser\nBankID auth"]
        Notifications["expo-notifications\nPush alerts"]
        Linking["expo-linking\ndrop:// deep links"]
        Biometrics["expo-local-authentication\nPhase 2 — app unlock"]
    end

    subgraph "Backend"
        HonoAPI["Hono v4 REST API\n/v1/* Bearer token auth"]
        BankID["BankID OIDC\nAuthentication"]
        OpenBanking["Open Banking (PSD2)\nAISP + PISP"]
    end

    Screens --> Nav
    Screens --> State
    State --> APIClient
    APIClient --> AsyncStorage
    APIClient --> HonoAPI
    Screens --> Camera
    Screens --> WebBrowser
    WebBrowser --> BankID
    BankID --> Linking
    HonoAPI --> OpenBanking
    Notifications --> Screens

Approval

Role Name Date Signature
Author John (AI Director) 2026-02-23
Mobile Lead
Tech Lead
Product Owner