# 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

```mermaid
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)

**Deep link handling:**
- URL scheme: `drop://`
- BankID callback: `drop://auth/callback?code=&state=`
- Library: `expo-linking`
- Config: `app.config.ts` → `scheme: "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):**
```javascript
// 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

```mermaid
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

```mermaid
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 | | | |