docs: Thêm tài liệu kiến trúc ARCHITECTURE.md và cập nhật README.md.

This commit is contained in:
Ho Ngoc Hai
2026-01-16 10:53:16 +07:00
parent f62464bc36
commit c437ea4c9f
5 changed files with 642 additions and 116 deletions

View File

@@ -0,0 +1,301 @@
# 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
```swift
@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**:
```swift
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:
```swift
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:**
```swift
enum AuthState {
case unknown // Initial state / Trng thái khi to
case unauthenticated // Logged out / Chưa đăng nhp
case authenticated(User) // Logged in / Đã đăng nhp
}
```
### 3. Data Layer
#### Models
```swift
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
```mermaid
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
```mermaid
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?
```swift
// 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?
```swift
@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 |
## Related Documentation / Tài Liệu Liên Quan
- [README.md](./README.md) - Quick start guide
- [Swift Enterprise Architect Skill](../../.agent/skills/swift-enterprise-architect/SKILL.md)
- [Swift Security Skill](../../.agent/skills/swift-security/SKILL.md)
- [Swift Networking Skill](../../.agent/skills/swift-networking/SKILL.md)

View File

@@ -14,19 +14,28 @@ import SwiftUI
/// API configuration constants
/// Các hng s cu hình API
enum APIConfig {
/// Base URL for API requests
/// URL gc cho các request API
static let baseURL = "https://api.goodgo.vn"
/// Base URL for API requests (Traefik gateway)
/// URL gc cho các request API (Traefik gateway)
static let baseURL = "http://localhost"
/// API version prefix
/// Tin t phiên bn API
static let apiVersion = "/api/v1"
/// OAuth2 token endpoint (no version prefix)
/// OAuth2 token endpoint (không có version prefix)
static let tokenEndpoint = "/connect/token"
/// OAuth2 scope for authentication
/// OAuth2 scope cho xác thc
static let oauthScope = "openid profile email offline_access"
/// Request timeout in seconds
/// Thi gian timeout request (giây)
static let timeout: TimeInterval = 30.0
}
// MARK: - App Constants
// Hng s ng dng

View File

@@ -51,6 +51,39 @@ enum APIError: Error, LocalizedError {
}
}
// MARK: - OAuth2 Response Models
// Models response OAuth2
/// OAuth2 token response
/// Response token OAuth2
struct OAuthTokenResponse: Decodable {
let accessToken: String
let tokenType: String
let expiresIn: Int
let refreshToken: String?
let scope: String?
enum CodingKeys: String, CodingKey {
case accessToken = "access_token"
case tokenType = "token_type"
case expiresIn = "expires_in"
case refreshToken = "refresh_token"
case scope
}
}
/// OAuth2 error response
/// Response li OAuth2
struct OAuthErrorResponse: Decodable {
let error: String
let errorDescription: String?
enum CodingKeys: String, CodingKey {
case error
case errorDescription = "error_description"
}
}
// MARK: - HTTP Method
// Phương thc HTTP
@@ -232,4 +265,61 @@ final class APIService: APIServiceProtocol {
func delete<T: Decodable>(endpoint: String) async throws -> T {
try await request(endpoint: endpoint, method: .delete, body: nil as String?, headers: nil)
}
// MARK: - OAuth2 Methods
// Các phương thc OAuth2
/// POST form-urlencoded request (for OAuth2 token endpoint)
/// Request POST dng form-urlencoded (cho OAuth2 token endpoint)
/// - Parameters:
/// - endpoint: Token endpoint path / Đưng dn token endpoint
/// - formData: Form data dictionary / Dictionary form data
/// - Returns: Decoded response / Response đã gii mã
func postForm<T: Decodable>(endpoint: String, formData: [String: String]) async throws -> T {
guard let url = URL(string: APIConfig.baseURL + endpoint) else {
throw APIError.invalidURL
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.timeoutInterval = APIConfig.timeout
// Encode form data
// Mã hóa form data
let formBody = formData.map { key, value in
let escapedValue = value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? ""
return "\(key)=\(escapedValue)"
}.joined(separator: "&")
request.httpBody = formBody.data(using: .utf8)
// Perform request
// Thc hin request
let (data, response) = try await session.data(for: request)
guard let httpResponse = response as? HTTPURLResponse else {
throw APIError.unknown
}
// Handle OAuth2 error responses
// X lý OAuth2 error responses
if httpResponse.statusCode != 200 {
if let errorResponse = try? decoder.decode(OAuthErrorResponse.self, from: data) {
throw APIError.serverError(
statusCode: httpResponse.statusCode,
message: errorResponse.errorDescription ?? errorResponse.error
)
}
let message = String(data: data, encoding: .utf8)
throw APIError.serverError(statusCode: httpResponse.statusCode, message: message)
}
do {
return try decoder.decode(T.self, from: data)
} catch {
throw APIError.decodingError(error)
}
}
try await request(endpoint: endpoint, method: .delete, body: nil as String?, headers: nil)
}
}

View File

@@ -1,164 +1,290 @@
# App Client Base Swift
# App Client Base Swift / Ứng Dụng Client iOS
Ứng dụng iOS native cho nền tảng GoodGo, xây dựng bằng Swift SwiftUI.
> **EN**: Native iOS client application for GoodGo platform, built with Swift and SwiftUI following MVVM architecture.
> **VI**: Ứng dụng iOS native cho nền tảng GoodGo, xây dựng bằng Swift và SwiftUI theo kiến trúc MVVM.
## 📱 Tổng quan
## 📱 Features / Tính Năng
App client iOS native sử dụng kiến trúc MVVM với SwiftUI, tương tự như `app-client-base-net` (MAUI) nhưng dành riêng cho nền tảng Apple.
| Feature / Tính năng | Description / Mô tả |
|---------------------|---------------------|
| 🔐 Authentication | Login, Register, Forgot Password với form validation |
| 🏠 Home Dashboard | Greeting động, Featured items, Activity feed |
| 🔍 Explore | Khám phá địa điểm và dịch vụ |
| 👤 Profile | Quản lý thông tin cá nhân và cài đặt |
| 🌓 Dark Mode | Hỗ trợ chế độ tối tự động |
| 🌐 i18n | Đa ngôn ngữ (Tiếng Việt & English) |
## 🛠️ Công nghệ
## 🛠️ Tech Stack / Công Nghệ
| Công nghệ | Phiên bản | Mục đích |
|-----------|-----------|----------|
| Swift | 5.9+ | Ngôn ngữ lập trình |
| SwiftUI | iOS 15+ | UI Framework |
| Xcode | 15+ | IDE |
| URLSession | Native | Networking |
| Keychain | Native | Secure Storage |
| Technology | Version | Purpose / Mục đích |
|------------|---------|-------------------|
| Swift | 5.9+ | Primary language / Ngôn ngữ chính |
| SwiftUI | iOS 15+ | Declarative UI framework |
| Xcode | 15.0+ | IDE development |
| URLSession | Native | HTTP networking |
| Keychain | Native | Secure token storage / Lưu trữ token bảo mật |
| Combine | Native | Reactive programming |
## 📂 Cấu trúc thư mục
## <EFBFBD> Prerequisites / Yêu Cầu
- **macOS**: 14.0+ (Sonoma)
- **Xcode**: 15.0+
- **iOS Target**: 15.0+
- **Apple Developer Account**: Required for device deployment / Cần thiết cho deploy lên thiết bị
## 🚀 Quick Start / Bắt Đầu Nhanh
### 1. Clone và mở project
```bash
cd apps/app-client-base-swift
open AppClientBaseSwift/AppClientBaseSwift.xcodeproj
```
### 2. Chọn Simulator
- Xcode menu: **Product > Destination > iPhone 15 Pro** (hoặc simulator khác)
### 3. Build và Run
```bash
# Sử dụng shortcut
⌘R (Command + R)
# Hoặc từ terminal
xcodebuild -project AppClientBaseSwift/AppClientBaseSwift.xcodeproj \
-scheme AppClientBaseSwift \
-destination 'platform=iOS Simulator,name=iPhone 15 Pro' \
build
```
### 4. Mock Login (để test)
```
Email: admin@goodgo.com
Password: 123456
```
## 📂 Project Structure / Cấu Trúc Project
```
AppClientBaseSwift/
├── App/
│ └── AppClientBaseSwiftApp.swift # @main entry point
│ └── AppClientBaseSwiftApp.swift # @main entry point
├── Core/
│ ├── Constants/
│ │ └── Constants.swift # App-wide constants
│ │ └── Constants.swift # API, App, Storage, DesignSystem
│ └── Extensions/
│ ├── View+Extensions.swift # SwiftUI View helpers
│ └── String+Extensions.swift # String utilities
│ ├── View+Extensions.swift # SwiftUI modifiers
│ └── String+Extensions.swift # Validation, formatting
├── Models/
│ └── User.swift # User data model
├── ViewModels/
│ └── HomeViewModel.swift # MVVM ViewModel
│ └── User.swift # User entity + extensions
├── ViewModels/ # MVVM ViewModels
│ ├── AuthViewModel.swift # Login/Register/ForgotPassword
│ ├── HomeViewModel.swift # Home screen logic
│ └── ProfileViewModel.swift # Profile management
├── Views/
── Screens/
├── ContentView.swift # Main tab container
├── WelcomeView.swift # Onboarding screen
├── HomeView.swift # Home tab
── ExploreView.swift # Explore tab
└── ProfileView.swift # Profile tab
── Auth/ # Authentication screens
├── AuthContainerView.swift # Auth navigation container
├── LoginView.swift # Login UI
├── RegisterView.swift # Registration UI
── ForgotPasswordView.swift # Password reset UI
│ ├── Home/ # Home components
│ │ ├── WalletCard.swift # Wallet balance card
│ │ ├── PromoCarousel.swift # Promotions carousel
│ │ ├── ServiceGrid.swift # Services grid
│ │ └── ActivityFeed.swift # Recent activities
│ │
│ └── Screens/ # Main screens
│ ├── ContentView.swift # Root container + TabBar
│ ├── SplashView.swift # Splash animation
│ ├── WelcomeView.swift # Onboarding
│ ├── HomeView.swift # Home tab
│ ├── ExploreView.swift # Explore tab
│ └── ProfileView.swift # Profile tab
├── Services/
│ ├── APIService.swift # HTTP client
│ └── AuthManager.swift # Authentication
├── Resources/
│ ├── Assets.xcassets/ # Images & Colors
├── en.lproj/ # English strings
── vi.lproj/ # Vietnamese strings
└── Info.plist # App configuration
│ ├── APIService.swift # HTTP client với URLSession
│ └── AuthManager.swift # Auth state + Keychain
└── Resources/
├── Assets.xcassets/ # Images & Colors
── en.lproj/ # English localization
└── vi.lproj/ # Vietnamese localization
```
## 🚀 Bắt đầu
### Yêu cầu
- macOS 14.0+ (Sonoma)
- Xcode 15.0+
- iOS 15.0+ target
### Mở project
1. Mở Xcode
2. Chọn **File > Open** hoặc nhấn `⌘O`
3. Chọn thư mục `app-client-base-swift`
4. Build và chạy trên Simulator (`⌘R`)
### Tạo Xcode Project (Nếu chưa có)
Do project này chỉ chứa source files, bạn cần tạo Xcode project:
```bash
# Mở Xcode và tạo project mới
# 1. File > New > Project
# 2. Chọn "App" template
# 3. Product Name: AppClientBaseSwift
# 4. Team: Chọn team của bạn
# 5. Organization Identifier: vn.goodgo
# 6. Interface: SwiftUI
# 7. Language: Swift
# 8. Lưu vào thư mục app-client-base-swift
# 9. Xóa các file generated và thay thế bằng files trong thư mục này
```
## 🎨 Kiến trúc
## 🎨 Architecture / Kiến Trúc
### MVVM Pattern
```
┌─────────────────────────────────────────────────────────────┐
VIEW (SwiftUI) │
ContentView, HomeView, ExploreView, ProfileView
│ VIEW (SwiftUI)
HomeView, ProfileView, AuthContainerView, LoginView...
├─────────────────────────────────────────────────────────────┤
│ ┌──────────────────────────────────┐ │
│ │ @StateObject │ │
│ │ @EnvironmentObject │ │
│ └──────────────────────────────────┘ │
@StateObject / @EnvironmentObject
│ │ │
├────────────────────────────▼────────────────────────────────┤
VIEWMODEL
HomeViewModel (ObservableObject)
- @Published properties
- async/await methods
│ VIEWMODEL (ObservableObject)
│ HomeViewModel, AuthViewModel, ProfileViewModel
@Published properties for reactive UI
async/await methods for data loading
│ • Business logic and validation │
├─────────────────────────────────────────────────────────────┤
│ ┌──────────────────────────────────┐ │
│ │ Protocol / Dependency │ │
│ │ Injection │ │
│ └──────────────────────────────────┘ │
Protocol-based Dependency Injection
│ │ │
├────────────────────────────▼────────────────────────────────┤
│ SERVICES
APIService, AuthManager
SERVICES │
│ APIService (HTTP) • AuthManager (Auth State + Keychain)
└─────────────────────────────────────────────────────────────┘
```
### Key Features
### Authentication Flow
- **SwiftUI**: UI declarative hiện đại
- **Async/Await**: Xử lý bất đồng bộ native
- **Keychain**: Lưu trữ token bảo mật
- **Localization**: Hỗ trợ đa ngôn ngữ (VI/EN)
- **Dark Mode**: Hỗ trợ giao diện tối
```mermaid
stateDiagram-v2
[*] --> SplashScreen
SplashScreen --> CheckAuth: App Launch
CheckAuth --> Authenticated: Token Valid
CheckAuth --> Unauthenticated: No Token
Unauthenticated --> Login
Login --> Authenticated: Success
Login --> Register: Sign Up
Register --> Authenticated: Success
Authenticated --> HomeScreen
HomeScreen --> Unauthenticated: Logout
```
## 📋 Conventions
### Data Flow
### Coding Style
```
User Action → View → ViewModel.method() → Service.request() → API
@Published update
View rerender
```
## 📋 Coding Conventions / Quy Ước Code
### File Structure
```swift
// MARK: - Imports
import SwiftUI
// MARK: - Type Definition
/// Description in English
/// Mô t bng tiếng Vit
struct/class/enum TypeName {
// MARK: - Properties
// MARK: - Init
// MARK: - Public Methods
// MARK: - Private Methods
}
// MARK: - Extensions
// MARK: - Preview Provider (DEBUG only)
```
### ViewModel Pattern
```swift
// ViewModel vi ObservableObject
@MainActor
class HomeViewModel: ObservableObject {
@Published var isLoading: Bool = false
final class FeatureViewModel: ObservableObject {
// Published properties for UI binding
@Published var isLoading = false
@Published var errorMessage: String?
@Published var data: [Model] = []
// Dependencies via init
private let apiService: APIServiceProtocol
init(apiService: APIServiceProtocol = APIService.shared) {
self.apiService = apiService
}
// Async methods
func loadData() async {
// Async data loading
}
}
// View vi MVVM binding
struct HomeView: View {
@StateObject private var viewModel = HomeViewModel()
var body: some View {
// SwiftUI body
isLoading = true
defer { isLoading = false }
do {
data = try await apiService.get(endpoint: "/data")
} catch {
errorMessage = error.localizedDescription
}
}
}
```
### Comments (Bilingual)
### Bilingual Comments
```swift
/// Load home screen data
/// Ti d liu màn hình home
func loadData() async { }
/// Load user profile data
/// Ti d liu h sơ ngưi dùng
func loadProfile() async { }
```
## 🔗 Liên kết
## ⚙️ Configuration / Cấu Hình
- [app-client-base-net](../app-client-base-net) - .NET MAUI client
- [web-client](../web-client) - Web client
### API Configuration
```swift
// Core/Constants/Constants.swift
enum APIConfig {
static let baseURL = "https://api.goodgo.vn"
static let apiVersion = "/api/v1"
static let timeout: TimeInterval = 30.0
}
```
### Environment Variables
| Key | Description / Mô tả | Default |
|-----|---------------------|---------|
| `API_BASE_URL` | Backend API URL | `https://api.goodgo.vn` |
| `API_VERSION` | API version prefix | `/api/v1` |
## 🧪 Testing / Kiểm Thử
### Run Unit Tests
```bash
xcodebuild test \
-project AppClientBaseSwift/AppClientBaseSwift.xcodeproj \
-scheme AppClientBaseSwift \
-destination 'platform=iOS Simulator,name=iPhone 15 Pro'
```
### Test Plan
Located at: `AppClientBaseSwift.xctestplan`
## 🔐 Security / Bảo Mật
| Feature | Implementation / Triển khai |
|---------|----------------------------|
| Token Storage | Keychain Services (not UserDefaults) |
| Secure Requests | HTTPS only, Bearer token auth |
| Session Management | Auto token refresh, secure logout |
| Data Protection | Sensitive data encrypted at rest |
## 📱 Supported Devices / Thiết Bị Hỗ Trợ
- **iPhone**: 8 and later (iOS 15+)
- **iPad**: All iPads with iOS 15+
- **Orientations**: Portrait (primary), Landscape (supported)
## 🔗 Related Projects / Dự Án Liên Quan
- [app-client-base-net](../app-client-base-net) - .NET MAUI cross-platform client
- [iam-service-net](../../services/iam-service-net) - Authentication backend
- [web-client](../web-client) - Web application
## 📚 Additional Documentation / Tài Liệu Bổ Sung
- [ARCHITECTURE.md](./ARCHITECTURE.md) - Chi tiết kiến trúc và design decisions
- [Swift Enterprise Skills](../../.agent/skills/swift-enterprise-architect/SKILL.md) - Swift development guidelines
## 📄 License