Files
pos-system/apps/app-client-base-swift/ARCHITECTURE.md

16 KiB

Architecture / Kiến Trúc

EN: Detailed architecture documentation for AppClientBaseSwift iOS application. VI: Tài liệu kiến trúc chi tiết cho ứng dụng iOS AppClientBaseSwift.

Overview / Tổng Quan

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 / Sơ Đồ Kiến Trúc

┌─────────────────────────────────────────────────────────────────────────┐
│                           PRESENTATION LAYER                             │
│  ┌─────────────────────────────────────────────────────────────────────┐ │
│  │                         SwiftUI Views                                │ │
│  │  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐   │ │
│  │  │ SplashView  │ │  HomeView   │ │ ExploreView │ │ ProfileView │   │ │
│  │  └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘   │ │
│  │  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐                   │ │
│  │  │  LoginView  │ │RegisterView │ │ForgotPasswd │                   │ │
│  │  └─────────────┘ └─────────────┘ └─────────────┘                   │ │
│  └─────────────────────────────────────────────────────────────────────┘ │
│                                    │                                      │
│                        @StateObject / @EnvironmentObject                  │
│                                    ▼                                      │
│  ┌─────────────────────────────────────────────────────────────────────┐ │
│  │                        ViewModels (@MainActor)                       │ │
│  │  ┌────────────────┐ ┌────────────────┐ ┌────────────────┐           │ │
│  │  │ AuthViewModel  │ │ HomeViewModel  │ │ProfileViewModel│           │ │
│  │  │ @Published     │ │ @Published     │ │ @Published     │           │ │
│  │  │ - isLoading    │ │ - items        │ │ - user         │           │ │
│  │  │ - errorMessage │ │ - greeting     │ │ - isEditing    │           │ │
│  │  └────────────────┘ └────────────────┘ └────────────────┘           │ │
│  └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
                                    │
                    Dependency Injection (Protocol-based)
                                    ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                            SERVICE LAYER                                 │
│  ┌─────────────────────────────┐  ┌─────────────────────────────┐       │
│  │        APIService           │  │       AuthManager            │       │
│  │  ┌───────────────────────┐  │  │  ┌───────────────────────┐   │       │
│  │  │ APIServiceProtocol    │  │  │  │ @Published authState  │   │       │
│  │  │ - request<T>()        │  │  │  │ - login()             │   │       │
│  │  │ - get(), post()       │  │  │  │ - register()          │   │       │
│  │  │ - put(), delete()     │  │  │  │ - logout()            │   │       │
│  │  └───────────────────────┘  │  │  │ - refreshToken()      │   │       │
│  │        URLSession           │  │  │       Keychain         │   │       │
│  └─────────────────────────────┘  └─────────────────────────────┘       │
└─────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                             DATA LAYER                                   │
│  ┌─────────────────────────────┐  ┌─────────────────────────────┐       │
│  │          Models             │  │        Constants             │       │
│  │  ┌───────────────────────┐  │  │  ┌───────────────────────┐   │       │
│  │  │ User (Codable)        │  │  │  │ APIConfig             │   │       │
│  │  │ HomeItem              │  │  │  │ AppConstants          │   │       │
│  │  │ AuthState             │  │  │  │ StorageKeys           │   │       │
│  │  └───────────────────────┘  │  │  │ DesignSystem          │   │       │
│  └─────────────────────────────┘  └─────────────────────────────┘       │
└─────────────────────────────────────────────────────────────────────────┘

Component Details / Chi Tiết Component

1. Presentation Layer

Views

Component Responsibility / Trách nhiệm
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 {
    // Reactive properties
    @Published var isLoading: Bool = false
    @Published var items: [HomeItem] = []
    @Published var errorMessage: String?
    
    // Dependencies injected via init
    private let apiService: APIServiceProtocol
    
    // Async methods using Swift Concurrency
    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
  • Error categorization

AuthManager

Singleton for authentication state:

final class AuthManager: ObservableObject {
    @MainActor static let shared = AuthManager()
    
    @Published var authState: AuthState = .unknown
    
    // Keychain-backed tokens
    var accessToken: String? { get }
    var refreshToken: String? { get }
}

AuthState Enum:

enum AuthState {
    case unknown        // Initial state / Trạng thái khởi tạo
    case unauthenticated // Logged out / Chưa đăng nhập
    case authenticated(User) // Logged in / Đã đăng nhập
}

3. Data Layer

Models

struct User: Codable, Identifiable, Equatable {
    let id: String
    let email: String
    let name: String
    let avatarUrl: String?
    let phoneNumber: String?
    let isEmailVerified: Bool
    let createdAt: Date?
    let updatedAt: Date?
}

Constants

Organized into semantic enums:

  • APIConfig: Base URL, version, timeout
  • AppConstants: App name, bundle ID, keychain service
  • StorageKeys: UserDefaults/Keychain keys
  • DesignSystem: Spacing, corner radius, font sizes

Data Flow / Luồng Dữ Liệu

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 / Luồng Xác Thực

stateDiagram-v2
    [*] --> Unknown: App Launch
    Unknown --> Authenticated: Token Found + Valid
    Unknown --> Unauthenticated: No Token
    
    Unauthenticated --> Login: Show Login
    Login --> Authenticated: Login Success
    Login --> Register: Navigate
    Register --> Authenticated: Register Success
    
    Authenticated --> HomeScreen: Show Main App
    HomeScreen --> Unauthenticated: Logout
    
    Authenticated --> TokenRefresh: Token Expired
    TokenRefresh --> Authenticated: Refresh Success
    TokenRefresh --> Unauthenticated: Refresh Failed

Design Decisions / Quyết Định Thiết Kế

1. Why MVVM? / Tại Sao MVVM?

Benefit / Lợi ích Description / Mô tả
Testability ViewModel có thể test độc lập không cần UI
Separation of Concerns View chỉ hiển thị, logic nằm ở ViewModel
SwiftUI Compatibility @ObservableObject + @Published native
Reactive Updates Combine-based automatic UI refresh

2. Why Protocol-based DI?

// Protocol enables mocking for tests
protocol APIServiceProtocol {
    func get<T: Decodable>(endpoint: String) async throws -> T
}

// Production implementation
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?

@MainActor
final class HomeViewModel: ObservableObject { ... }
  • Ensures all @Published updates happen on main thread
  • Prevents concurrency issues with SwiftUI
  • Explicit thread safety contract

Security Architecture / Kiến Trúc Bảo Mật

┌────────────────────────────────────────────────────────────┐
│                    SECURITY LAYERS                          │
├────────────────────────────────────────────────────────────┤
│  Layer 1: Transport Security (HTTPS/TLS)                   │
│  • All API calls use HTTPS                                 │
│  • Certificate pinning (TODO)                              │
├────────────────────────────────────────────────────────────┤
│  Layer 2: Token Security (Keychain)                        │
│  • Access token stored in Keychain                         │
│  • Refresh token stored in Keychain                        │
│  • kSecClassGenericPassword protection                     │
├────────────────────────────────────────────────────────────┤
│  Layer 3: Session Security                                 │
│  • Token expiry validation                                 │
│  • Automatic token refresh                                 │
│  • Secure logout (clear all tokens)                        │
├────────────────────────────────────────────────────────────┤
│  Layer 4: Input Validation                                 │
│  • Email format validation                                 │
│  • Password strength checking                              │
│  • Form field sanitization                                 │
└────────────────────────────────────────────────────────────┘

Future Considerations / Hướng Phát Triển

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
Analytics Low Event tracking system