Files
pos-system/microservices/.agent/skills/swift-enterprise-architect/SKILL.md
Ho Ngoc Hai 76d75c753b Migrate
2026-05-23 18:37:02 +07:00

450 lines
13 KiB
Markdown

---
name: swift-enterprise-architect
description: Kiến trúc và patterns cho ứng dụng SwiftUI Enterprise (MVVM, DI, Navigation, Project Structure). Use for iOS/macOS apps, SwiftUI development, hoặc khi cần structured Swift architecture.
compatibility: "Swift 5.9+, iOS 17+, macOS 14+, SwiftUI"
metadata:
author: Velik Ho
version: "1.0"
references: "Apple SwiftUI Documentation, Swift Concurrency"
---
# Swift Enterprise Development Workflow
Quy trình 4 giai đoạn để phát triển ứng dụng SwiftUI theo chuẩn Enterprise.
## When to Use This Skill / Khi Nào Sử Dụng
Use this skill when:
- Building iOS/macOS/visionOS apps / Xây dựng app Apple platforms
- Creating enterprise SwiftUI applications / Tạo ứng dụng SwiftUI enterprise
- Need MVVM + DI architecture / Cần kiến trúc MVVM + DI
- Implementing navigation patterns / Triển khai điều hướng
**DO NOT use when:**
- Simple single-screen apps / App đơn giản 1 màn hình
- UIKit-only projects / Dự án chỉ UIKit
- Backend Swift (use Vapor patterns) / Swift backend
## Overview / Tổng Quan
```
┌──────────────────────────────────────────────────────────────────┐
│ WORKFLOW 4 GIAI ĐOẠN (Swift Enterprise) │
├──────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────────┐ │
│ │ PHASE 1 │────►│ PHASE 2 │ │
│ │ PROJECT │ │ ARCHITECTURE │ │
│ │ STRUCTURE │ │ │ │
│ │ - Folders │ │ - MVVM Pattern │ │
│ │ - Resources │ │ - DI Setup │ │
│ │ - Config │ │ - Services │ │
│ └─────────────┘ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ ┌─────────────────┐ │
│ │ PHASE 4 │◄────│ PHASE 3 │ │
│ │ PLATFORM │ │ UI & NAV │ │
│ │ │ │ │ │
│ │ - Extensions│ │ - TabView │ │
│ │ - Platform │ │ - NavStack │ │
│ │ - Native │ │ - Sheets │ │
│ └─────────────┘ └─────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────┘
```
---
## Phase 1: Project Structure / Cấu Trúc Dự Án
**Goal**: Thiết lập cấu trúc thư mục chuẩn Enterprise
### Project Structure
```
MyApp/
├── MyAppApp.swift # App entry point
├── ContentView.swift # Root view
├── Core/ # Core utilities
│ ├── Constants/
│ │ └── Constants.swift # App-wide constants
│ └── Extensions/
│ ├── String+Extensions.swift
│ └── View+Extensions.swift
├── Models/ # Data models
│ └── User.swift
├── Services/ # Business services
│ ├── APIService.swift
│ └── AuthManager.swift
├── ViewModels/ # MVVM ViewModels
│ ├── AuthViewModel.swift
│ └── HomeViewModel.swift
├── Views/ # SwiftUI Views
│ ├── Auth/
│ │ ├── LoginView.swift
│ │ └── RegisterView.swift
│ ├── Home/
│ │ └── HomeView.swift
│ └── Screens/
│ ├── ProfileView.swift
│ └── SettingsView.swift
└── Resources/
├── Assets.xcassets/
└── Localizable.strings
```
### Constants Pattern
```swift
// Core/Constants/Constants.swift
// MARK: - API Configuration
enum APIConfig {
static let baseURL = "https://api.example.com"
static let apiVersion = "/api/v1"
static let timeout: TimeInterval = 30.0
}
// MARK: - Storage Keys
enum StorageKeys {
static let accessToken = "access_token"
static let refreshToken = "refresh_token"
static let userData = "user_data"
}
// MARK: - Design System
enum DesignSystem {
// Spacing
static let spacingXS: CGFloat = 4
static let spacingSM: CGFloat = 8
static let spacingMD: CGFloat = 16
static let spacingLG: CGFloat = 24
// Corner Radius
static let cornerRadiusSM: CGFloat = 8
static let cornerRadiusMD: CGFloat = 12
static let cornerRadiusLG: CGFloat = 16
}
```
---
## Phase 2: Architecture (MVVM + DI) / Kiến Trúc
**Goal**: Thiết lập MVVM pattern với Swift Concurrency
### ViewModel Pattern (BẮT BUỘC)
```swift
// ViewModels/SomeViewModel.swift
import SwiftUI
import Combine
/// ViewModel for SomeView
/// ViewModel cho SomeView
@MainActor
final class SomeViewModel: ObservableObject {
// MARK: - Published Properties
/// Current items
/// Các items hin ti
@Published private(set) var items: [Item] = []
/// Loading state
/// Trng thái đang ti
@Published private(set) var isLoading = false
/// Error message
/// Thông báo li
@Published var errorMessage: String?
// MARK: - Dependencies
private let service: SomeServiceProtocol
// MARK: - Init
/// Initialize with dependencies
/// Khi to vi dependencies
init(service: SomeServiceProtocol = SomeService.shared) {
self.service = service
}
// MARK: - Public Methods
/// Load items from service
/// Ti items t service
func loadItems() async {
isLoading = true
errorMessage = nil
do {
items = try await service.fetchItems()
} catch {
errorMessage = error.localizedDescription
}
isLoading = false
}
}
```
### Service Pattern
```swift
// Services/SomeService.swift
/// Service protocol for DI
/// Protocol service cho DI
protocol SomeServiceProtocol {
func fetchItems() async throws -> [Item]
}
/// Main service implementation
/// Implementation service chính
final class SomeService: SomeServiceProtocol {
/// Shared singleton
/// Singleton dùng chung
static let shared = SomeService()
private init() {}
func fetchItems() async throws -> [Item] {
// Implementation
}
}
```
### Dependency Injection via Init
```swift
// GOOD: Protocol-based DI
final class HomeViewModel: ObservableObject {
private let authManager: AuthManagerProtocol
private let apiService: APIServiceProtocol
init(
authManager: AuthManagerProtocol = AuthManager.shared,
apiService: APIServiceProtocol = APIService.shared
) {
self.authManager = authManager
self.apiService = apiService
}
}
// Testing
let mockAuth = MockAuthManager()
let viewModel = HomeViewModel(authManager: mockAuth)
```
---
## Phase 3: UI & Navigation / Giao Diện & Điều Hướng
**Goal**: Xây dựng UI với TabView và NavigationStack
### TabView Navigation
```swift
// ContentView.swift
struct ContentView: View {
/// Selected tab
@State private var selectedTab: Tab = .home
/// Tab enumeration
enum Tab: String, CaseIterable {
case home, explore, profile
var title: String {
switch self {
case .home: return "Home"
case .explore: return "Explore"
case .profile: return "Profile"
}
}
var icon: String {
switch self {
case .home: return "house"
case .explore: return "magnifyingglass"
case .profile: return "person"
}
}
}
var body: some View {
TabView(selection: $selectedTab) {
HomeView()
.tabItem { Label(Tab.home.title, systemImage: Tab.home.icon) }
.tag(Tab.home)
ExploreView()
.tabItem { Label(Tab.explore.title, systemImage: Tab.explore.icon) }
.tag(Tab.explore)
ProfileView()
.tabItem { Label(Tab.profile.title, systemImage: Tab.profile.icon) }
.tag(Tab.profile)
}
}
}
```
### NavigationStack with Path
```swift
// Views/Home/HomeView.swift
struct HomeView: View {
@StateObject private var viewModel = HomeViewModel()
@State private var navigationPath = NavigationPath()
var body: some View {
NavigationStack(path: $navigationPath) {
List(viewModel.items) { item in
NavigationLink(value: item) {
ItemRow(item: item)
}
}
.navigationTitle("Home")
.navigationDestination(for: Item.self) { item in
ItemDetailView(item: item)
}
.task {
await viewModel.loadItems()
}
}
}
}
```
### Auth State Conditional UI
```swift
// ContentView.swift with Auth State
struct ContentView: View {
@StateObject private var authManager = AuthManager.shared
var body: some View {
Group {
switch authManager.authState {
case .unknown:
ProgressView()
case .unauthenticated:
AuthContainerView()
case .authenticated:
MainTabView()
}
}
.task {
await authManager.initialize()
}
}
}
```
---
## Phase 4: Platform & Extensions / Nền Tảng & Extensions
### String Extensions
```swift
// Core/Extensions/String+Extensions.swift
extension String {
/// Localized string
/// Chui đã bn đa hóa
var localized: String {
NSLocalizedString(self, comment: "")
}
/// Email validation
/// Kim tra email hp l
var isValidEmail: Bool {
let regex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
return NSPredicate(format: "SELF MATCHES %@", regex).evaluate(with: self)
}
/// Trimmed string
/// Chui đã trim
var trimmed: String {
trimmingCharacters(in: .whitespacesAndNewlines)
}
}
```
### View Extensions
```swift
// Core/Extensions/View+Extensions.swift
extension View {
/// Apply modifier conditionally
/// Áp dng modifier có điu kin
@ViewBuilder
func `if`<Content: View>(_ condition: Bool, transform: (Self) -> Content) -> some View {
if condition {
transform(self)
} else {
self
}
}
/// Hide keyboard
/// n bàn phím
func hideKeyboard() {
UIApplication.shared.sendAction(
#selector(UIResponder.resignFirstResponder),
to: nil, from: nil, for: nil
)
}
}
```
---
## Common Mistakes / Lỗi Thường Gặp
| Mistake | Problem | Solution |
|---------|---------|----------|
| Logic in View | Hard to test | Move to ViewModel |
| Missing `@MainActor` | Thread issues | Add to ViewModel class |
| Force unwrap `!` | Crashes | Use `if let`, `guard let` |
| Singleton abuse | Hard to test | Protocol-based DI |
| No loading states | Bad UX | Add `isLoading` property |
| Hardcoded strings | No i18n | Use `String.localized` |
## Quick Reference / Tham Chiếu Nhanh
| Category | Standard |
|----------|----------|
| Architecture | MVVM + Protocol DI |
| ViewModel | `@MainActor final class` + `ObservableObject` |
| State | `@Published`, `@State`, `@StateObject` |
| Navigation | `NavigationStack` + `NavigationPath` |
| Tabs | `TabView` with enum-based `Tab` |
| Async | `async/await`, `.task {}` modifier |
| Constants | Enum-based (no instance) |
| Comments | Bilingual EN/VI |
## Resources / Tài Nguyên
- [Swift Networking](../swift-networking/SKILL.md) - HTTP client patterns
- [Swift Security](../swift-security/SKILL.md) - Keychain & Auth
- [Swift UI Components](../swift-ui-components/SKILL.md) - Reusable components
- [Apple SwiftUI Docs](https://developer.apple.com/documentation/swiftui/)
- [Swift Concurrency](https://developer.apple.com/documentation/swift/concurrency)
- [Project Rules](../project-rules/SKILL.md) - GoodGo coding standards