Files
pos-system/apps/app-client-base-swift/docs/en/architecture.md

12 KiB

Architecture Guide

Detailed architecture documentation for AppClientBaseSwift iOS application.

Overview

AppClientBaseSwift is a native iOS application built using MVVM (Model-View-ViewModel) architecture pattern with SwiftUI for declarative UI. The app follows Apple's modern development best practices including:

  • Swift Concurrency (async/await)
  • Combine for reactive data binding
  • Protocol-oriented programming for testability
  • Keychain Services for secure storage

Architecture Diagram

┌─────────────────────────────────────────────────────────────────────────┐
│                           PRESENTATION LAYER                             │
│  ┌─────────────────────────────────────────────────────────────────────┐ │
│  │                         SwiftUI Views                                │ │
│  │  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐   │ │
│  │  │ SplashView  │ │  HomeView   │ │ ExploreView │ │ ProfileView │   │ │
│  │  └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘   │ │
│  │  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐                   │ │
│  │  │  LoginView  │ │RegisterView │ │ForgotPasswd │                   │ │
│  │  └─────────────┘ └─────────────┘ └─────────────┘                   │ │
│  └─────────────────────────────────────────────────────────────────────┘ │
│                        @StateObject / @EnvironmentObject                 │
│  ┌─────────────────────────────────────────────────────────────────────┐ │
│  │                        ViewModels (@MainActor)                       │ │
│  │  ┌────────────────┐ ┌────────────────┐ ┌────────────────┐           │ │
│  │  │ AuthViewModel  │ │ HomeViewModel  │ │ProfileViewModel│           │ │
│  │  └────────────────┘ └────────────────┘ └────────────────┘           │ │
│  └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
                    Dependency Injection (Protocol-based)
┌─────────────────────────────────────────────────────────────────────────┐
│                            SERVICE LAYER                                 │
│  ┌─────────────────────────────┐  ┌─────────────────────────────┐       │
│  │        APIService           │  │       AuthManager            │       │
│  │  • request<T>()             │  │  • @Published authState      │       │
│  │  • get(), post()            │  │  • login(), register()       │       │
│  │  • URLSession               │  │  • Keychain storage          │       │
│  └─────────────────────────────┘  └─────────────────────────────┘       │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│                             DATA LAYER                                   │
│  ┌─────────────────────────────┐  ┌─────────────────────────────┐       │
│  │          Models             │  │        Constants             │       │
│  │  • User (Codable)           │  │  • APIConfig                 │       │
│  │  • HomeItem                 │  │  • StorageKeys               │       │
│  │  • AuthState                │  │  • DesignSystem              │       │
│  └─────────────────────────────┘  └─────────────────────────────┘       │
└─────────────────────────────────────────────────────────────────────────┘

Component Details

1. Presentation Layer

Views

Component Responsibility
SplashView Animated splash screen, delayed navigation
ContentView Root TabView container, auth state routing
AuthContainerView Auth flow navigation (Login/Register/Forgot)
HomeView Home tab with greeting, promo, services
ExploreView Discovery and search features
ProfileView User profile and settings

ViewModels

@MainActor
final class HomeViewModel: ObservableObject {
    @Published var isLoading: Bool = false
    @Published var items: [HomeItem] = []
    @Published var errorMessage: String?
    
    private let apiService: APIServiceProtocol
    
    func loadData() async { ... }
}

2. Service Layer

APIService

HTTP client following Single Responsibility Principle:

protocol APIServiceProtocol {
    func request<T: Decodable>(
        endpoint: String,
        method: HTTPMethod,
        body: Encodable?,
        headers: [String: String]?
    ) async throws -> T
}

Features:

  • Generic request/response handling
  • Automatic JSON encoding/decoding (snake_case ↔ camelCase)
  • Bearer token injection
  • HTTP status code handling

AuthManager

Singleton for authentication state:

final class AuthManager: ObservableObject {
    @MainActor static let shared = AuthManager()
    @Published var authState: AuthState = .unknown
}

AuthState Enum:

enum AuthState {
    case unknown        // Initial state
    case unauthenticated // Logged out
    case authenticated(User) // Logged in
}

Data Flow

sequenceDiagram
    participant V as View
    participant VM as ViewModel
    participant S as Service
    participant API as Backend API

    V->>VM: User Action (button tap)
    VM->>VM: Set isLoading = true
    VM->>S: await service.request()
    S->>API: HTTP Request
    API-->>S: JSON Response
    S-->>VM: Decoded Model
    VM->>VM: Update @Published
    VM-->>V: SwiftUI re-render

Authentication Flow

stateDiagram-v2
    [*] --> Unknown: App Launch
    Unknown --> Authenticated: Token Valid
    Unknown --> Unauthenticated: No Token
    
    Unauthenticated --> Login
    Login --> Authenticated: Success
    Login --> Register: Navigate
    Register --> Authenticated: Success
    
    Authenticated --> HomeScreen
    HomeScreen --> Unauthenticated: Logout

Design Decisions

1. Why MVVM?

Benefit Description
Testability ViewModel can be tested independently without UI
Separation of Concerns View only displays, logic in ViewModel
SwiftUI Compatibility @ObservableObject + @Published are native
Reactive Updates Combine-based automatic UI refresh

2. Why Protocol-based DI?

// Protocol enables mocking for tests
protocol APIServiceProtocol { ... }

// Production
final class APIService: APIServiceProtocol { ... }

// Test mock
final class MockAPIService: APIServiceProtocol { ... }

3. Why Keychain over UserDefaults?

Keychain UserDefaults
Encrypted at rest Plain text
Secure enclave Accessible
App-specific Shared prefs

4. Why @MainActor on ViewModels?

  • Ensures all @Published updates happen on main thread
  • Prevents concurrency issues with SwiftUI
  • Explicit thread safety contract

Security Architecture

┌────────────────────────────────────────────────────────────┐
│                    SECURITY LAYERS                          │
├────────────────────────────────────────────────────────────┤
│  Layer 1: Transport Security (HTTPS/TLS)                   │
│  • All API calls use HTTPS                                 │
├────────────────────────────────────────────────────────────┤
│  Layer 2: Token Security (Keychain)                        │
│  • Access token stored in Keychain                         │
│  • Refresh token stored in Keychain                        │
├────────────────────────────────────────────────────────────┤
│  Layer 3: Session Security                                 │
│  • Token expiry validation                                 │
│  • Automatic token refresh                                 │
├────────────────────────────────────────────────────────────┤
│  Layer 4: Input Validation                                 │
│  • Email format validation                                 │
│  • Password strength checking                              │
└────────────────────────────────────────────────────────────┘

Future Considerations

Feature Priority Description
Certificate Pinning High TLS certificate validation
Biometric Auth High Face ID / Touch ID login
Offline Mode Medium Local caching with SwiftData
Push Notifications Medium APNs integration