@leejungkiin/awkit 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/VERSION +1 -1
- package/core/GEMINI.md.bak +168 -181
- package/package.json +2 -2
- package/schemas/brain-snapshot.json +167 -0
- package/skills/CATALOG.md +70 -0
- package/skills/beads-manager/SKILL.md +20 -1
- package/skills/memory-sync/SKILL.md +20 -2
- package/skills/nm-memory-audit/SKILL.md +157 -0
- package/skills/nm-memory-evolution/SKILL.md +202 -0
- package/skills/nm-memory-intake/SKILL.md +135 -0
- package/skills/nm-memory-sync/SKILL.md +184 -0
- package/skills/orchestrator/SKILL.md +41 -50
- package/skills/schemas/brain-snapshot.json +167 -0
- package/skills/skills/nm-memory-audit/SKILL.md +157 -0
- package/skills/skills/nm-memory-evolution/SKILL.md +202 -0
- package/skills/skills/nm-memory-intake/SKILL.md +135 -0
- package/skills/skills/nm-memory-sync/SKILL.md +184 -0
- package/skills/smali-to-kotlin/phase-0-discovery.md +128 -0
- package/skills/smali-to-kotlin/phase-1-architecture.md +166 -0
- package/skills/smali-to-kotlin/phase-2-blueprint-ui.md +347 -0
- package/skills/smali-to-kotlin/phase-2-blueprint.md +228 -0
- package/skills/smali-to-kotlin/phase-3-build.md +248 -0
- package/skills/smali-to-kotlin/phase-3-logic-build.md +268 -0
- package/skills/smali-to-kotlin/templates/app-map.md +101 -0
- package/skills/smali-to-kotlin/templates/architecture.md +142 -0
- package/skills/smali-to-kotlin/templates/blueprint.md +145 -0
- package/skills/smali-to-swift/phase-0-discovery.md +137 -0
- package/skills/smali-to-swift/phase-1-architecture.md +168 -0
- package/skills/smali-to-swift/phase-2-blueprint-ui.md +348 -0
- package/skills/smali-to-swift/phase-2-blueprint.md +173 -0
- package/skills/smali-to-swift/phase-3-build.md +257 -0
- package/skills/smali-to-swift/phase-3-logic-build.md +312 -0
- package/skills/smali-to-swift/templates/app-map.md +82 -0
- package/skills/smali-to-swift/templates/architecture.md +97 -0
- package/skills/smali-to-swift/templates/blueprint.md +82 -0
- package/skills/workflows/nm-import.md +73 -0
- package/skills/workflows/nm-recall.md +67 -0
- package/skills/workflows/nm-snapshot.md +69 -0
- package/workflows/_uncategorized/nm-import.md +73 -0
- package/workflows/_uncategorized/nm-recall.md +67 -0
- package/workflows/_uncategorized/nm-snapshot.md +69 -0
- package/workflows/_uncategorized/reverse-android-build.md +222 -0
- package/workflows/_uncategorized/reverse-android-design.md +139 -0
- package/workflows/_uncategorized/reverse-android-discover.md +150 -0
- package/workflows/_uncategorized/reverse-android-scan.md +158 -0
- package/workflows/_uncategorized/reverse-android.md +143 -0
- package/workflows/_uncategorized/reverse-ios-build.md +240 -0
- package/workflows/_uncategorized/reverse-ios-design.md +112 -0
- package/workflows/_uncategorized/reverse-ios-discover.md +120 -0
- package/workflows/_uncategorized/reverse-ios-scan.md +155 -0
- package/workflows/_uncategorized/reverse-ios.md +152 -0
- package/workflows/mobile/reverse-android-build.md +119 -79
- package/workflows/mobile/reverse-android-design.md +10 -7
- package/workflows/mobile/reverse-android.md +52 -46
- package/workflows/mobile/reverse-ios-build.md +161 -50
- package/workflows/mobile/reverse-ios-design.md +10 -1
- package/workflows/mobile/reverse-ios.md +49 -37
- package/skills/adaptive-language/SKILL.md +0 -189
- package/skills/ambient-brain/SKILL.md +0 -314
- package/skills/ambient-brain/brain-router.md +0 -185
- package/skills/ambient-brain/brain-templates.md +0 -201
- package/skills/context-help/SKILL.md +0 -180
- package/skills/error-translator/SKILL.md +0 -153
- package/skills/session-restore/SKILL.md +0 -240
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# 🗺️ Phase 0: Discovery (Satellite View) — iOS
|
|
2
|
+
|
|
3
|
+
> **Zoom Level:** 0 — Bird's Eye
|
|
4
|
+
> **Goal:** Create an "App Map" — understand WHAT the app is and WHAT it contains.
|
|
5
|
+
> **Output:** Diagrams, tables, maps. **NO CODE.**
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## ⛔ OUTPUT RULE
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
✅ ALLOWED: ASCII diagrams, Mermaid, tables, bullet lists, bash scan commands
|
|
13
|
+
❌ BLOCKED: Swift/ObjC code, function signatures, class definitions
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 📋 Sub-steps
|
|
19
|
+
|
|
20
|
+
### 0.1: Confirm Input
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
🍎 iOS Reverse Engineering bắt đầu!
|
|
24
|
+
|
|
25
|
+
Em cần biết:
|
|
26
|
+
1. Decrypted .app bundle ở đâu?
|
|
27
|
+
2. Class-dump headers ở đâu?
|
|
28
|
+
3. Tên app gốc? Bundle ID?
|
|
29
|
+
|
|
30
|
+
Chưa chuẩn bị?
|
|
31
|
+
→ bagbak -o ~/decrypted/ com.example.app
|
|
32
|
+
→ class-dump -H ~/decrypted/App.app -o ~/headers/
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 0.2: Structure Scan
|
|
36
|
+
|
|
37
|
+
Quét IPA structure để hiểu quy mô app:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Embedded frameworks
|
|
41
|
+
ls [app_bundle]/Frameworks/ 2>/dev/null
|
|
42
|
+
|
|
43
|
+
# Linked libraries (non-system)
|
|
44
|
+
otool -L [app_bundle]/[AppName] | grep -v /System | grep -v /usr/lib
|
|
45
|
+
|
|
46
|
+
# Header imports (from class-dump)
|
|
47
|
+
grep -rh "#import <" [headers_dir]/ | sort -u | head -30
|
|
48
|
+
grep -rh "@import " [headers_dir]/ | sort -u
|
|
49
|
+
|
|
50
|
+
# Count ViewControllers
|
|
51
|
+
grep -rl "UIViewController" [headers_dir]/ | wc -l
|
|
52
|
+
|
|
53
|
+
# Resource overview
|
|
54
|
+
ls [app_bundle]/*.car [app_bundle]/*.momd [app_bundle]/*.storyboardc 2>/dev/null
|
|
55
|
+
find [app_bundle] -name "*.json" -o -name "*.plist" | grep -v Info.plist | sort
|
|
56
|
+
|
|
57
|
+
# SDK identifiers in binary
|
|
58
|
+
strings [app_bundle]/[AppName] | grep -i "cocoapods\|carthage\|firebase\|facebook\|google" | head -20
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 0.3: Framework Detection
|
|
62
|
+
|
|
63
|
+
Scan for third-party frameworks. Ref: `framework-patterns.md`
|
|
64
|
+
|
|
65
|
+
**Phân loại thành 5 nhóm:**
|
|
66
|
+
|
|
67
|
+
| Category | Meaning | Action |
|
|
68
|
+
|----------|---------|--------|
|
|
69
|
+
| ✅ Reuse | Modern, maintained | Add via SPM |
|
|
70
|
+
| 🔄 Replace | Legacy/deprecated | Map to modern Swift |
|
|
71
|
+
| 🍏 Apple | System frameworks | Use SwiftUI equivalents |
|
|
72
|
+
| 📱 Native | C/C++ dylibs | Keep, bridging header |
|
|
73
|
+
| 🏷️ App Code | Original app logic | Rebuild in Swift |
|
|
74
|
+
|
|
75
|
+
### 0.4: Info.plist Analysis
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
plutil -p [app_bundle]/Info.plist
|
|
79
|
+
codesign -d --entitlements :- [app_bundle]/[AppName] 2>/dev/null
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Extract:
|
|
83
|
+
- Bundle ID, Display Name, Min iOS version
|
|
84
|
+
- Privacy permissions (NSCameraUsageDescription, NSLocationWhenInUseUsageDescription, etc.)
|
|
85
|
+
- URL Schemes (deep links)
|
|
86
|
+
- Capabilities from entitlements (push, Apple Pay, Sign In with Apple, etc.)
|
|
87
|
+
- Supported orientations
|
|
88
|
+
|
|
89
|
+
### 0.5: Screen Map
|
|
90
|
+
|
|
91
|
+
Analyze class-dump headers for ViewControllers:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# Find all ViewControllers
|
|
95
|
+
grep -rl "UIViewController" [headers_dir]/ | sort
|
|
96
|
+
|
|
97
|
+
# Find tab bar controllers
|
|
98
|
+
grep -rl "UITabBarController" [headers_dir]/
|
|
99
|
+
|
|
100
|
+
# Find navigation controllers
|
|
101
|
+
grep -rl "UINavigationController" [headers_dir]/
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Map VCs → future SwiftUI screens. Draw navigation flow.
|
|
105
|
+
|
|
106
|
+
### 0.6: Complexity Estimate
|
|
107
|
+
|
|
108
|
+
Rate each area 1-5 dots:
|
|
109
|
+
- Data Layer: ●●●○○ (APIs, DB, keychain)
|
|
110
|
+
- Core Logic: ●●○○○ (crypto, formatters, utils)
|
|
111
|
+
- UI Screens: ●●●●○ (screen count, complex layouts)
|
|
112
|
+
- SDK Integration: ●●○○○ (third-party, native libs)
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 📊 Output: App Map
|
|
117
|
+
|
|
118
|
+
Use template from `templates/app-map.md`. Must include:
|
|
119
|
+
|
|
120
|
+
1. **Identity** — bundle ID, min iOS, screen count
|
|
121
|
+
2. **Screen Flow** — navigation graph (ASCII or Mermaid)
|
|
122
|
+
3. **Framework Landscape** — categorized dependency list
|
|
123
|
+
4. **Complexity Estimate** — visual dots rating
|
|
124
|
+
5. **Key Observations** — anything notable
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## ✅ Gate
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
"🗺️ Đây là App Map. Anh review xem có gì cần điều chỉnh không?"
|
|
132
|
+
→ User approves → Proceed to Phase 1 (Architecture)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
*Phase 0: Discovery — No code, just understanding*
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# 🏗️ Phase 1: Architecture (District View) — iOS
|
|
2
|
+
|
|
3
|
+
> **Zoom Level:** 1 — Architecture Design
|
|
4
|
+
> **Goal:** Design the overall architecture BEFORE writing any code.
|
|
5
|
+
> **Input:** Completed App Map from Phase 0.
|
|
6
|
+
> **Output:** Architecture Blueprint (layer maps, feature tables). **NO CODE BODIES.**
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## ⛔ OUTPUT RULE
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
✅ ALLOWED: Architecture diagrams, layer maps, feature-to-file mapping tables
|
|
14
|
+
✅ ALLOWED: File paths, module names, dependency lists
|
|
15
|
+
❌ BLOCKED: Function bodies, implementation details, actual Swift code
|
|
16
|
+
⚠️ EXCEPTION: SPM dependency declarations only
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 📋 Sub-steps
|
|
22
|
+
|
|
23
|
+
### 1.1: Layer Design
|
|
24
|
+
|
|
25
|
+
Design Clean Architecture layers based on App Map:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
┌─────────────────────────────────────┐
|
|
29
|
+
│ Presentation │
|
|
30
|
+
│ ├── Screens/ ([N] screens) │
|
|
31
|
+
│ ├── Navigation/ (NavigationStack) │
|
|
32
|
+
│ ├── Theme/ (AppTheme) │
|
|
33
|
+
│ └── Components/ (Shared Views) │
|
|
34
|
+
├─────────────────────────────────────┤
|
|
35
|
+
│ Domain │
|
|
36
|
+
│ ├── Models/ ([N] business models) │
|
|
37
|
+
│ ├── Repositories/ ([N] protocols) │
|
|
38
|
+
│ └── UseCases/ ([N] use cases) │
|
|
39
|
+
├─────────────────────────────────────┤
|
|
40
|
+
│ Data │
|
|
41
|
+
│ ├── Network/ (APIClient, Endpoints) │
|
|
42
|
+
│ ├── Local/ (SwiftData, Keychain) │
|
|
43
|
+
│ └── Repositories/ (implementations)│
|
|
44
|
+
├─────────────────────────────────────┤
|
|
45
|
+
│ DI │
|
|
46
|
+
│ └── AppContainer.swift │
|
|
47
|
+
└─────────────────────────────────────┘
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 1.2: Feature → File Mapping
|
|
51
|
+
|
|
52
|
+
| Feature | Domain Model | Repository | UseCase | Screen(s) | ViewModel |
|
|
53
|
+
|---------|-------------|-----------|---------|-----------|-----------|
|
|
54
|
+
| Auth | User, Token | AuthRepo | LoginUC | Login, Register | AuthVM |
|
|
55
|
+
| Home | [Model] | [Repo] | [UC] | Home | HomeVM |
|
|
56
|
+
| ... | ... | ... | ... | ... | ... |
|
|
57
|
+
|
|
58
|
+
### 1.3: API Endpoint Inventory
|
|
59
|
+
|
|
60
|
+
Extract ALL API endpoints from class-dump headers + disassembly:
|
|
61
|
+
|
|
62
|
+
| # | Method | Endpoint | Auth | Notes |
|
|
63
|
+
|---|--------|----------|------|-------|
|
|
64
|
+
| 1 | POST | /auth/login | No | JWT response |
|
|
65
|
+
| 2 | GET | /users/me | Bearer | User profile |
|
|
66
|
+
|
|
67
|
+
**How to find in class-dump/disassembly:**
|
|
68
|
+
```
|
|
69
|
+
Look for: NSString init with "https://" or "http://"
|
|
70
|
+
Look for: properties named baseURL, apiURL, endpoint
|
|
71
|
+
Look for: method names like fetchUser, loginWith, getProfile
|
|
72
|
+
Look for: AFHTTPSessionManager or NSURLSession usage patterns
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 1.4: Data Schema Inventory
|
|
76
|
+
|
|
77
|
+
| Model | Key Fields | Source | Storage |
|
|
78
|
+
|-------|-----------|--------|---------|
|
|
79
|
+
| User | id, name, email, avatar | API + Local | SwiftData |
|
|
80
|
+
| Settings | theme, lang, notif | Local only | @AppStorage |
|
|
81
|
+
|
|
82
|
+
### 1.5: Xcode Project Structure
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
App/
|
|
86
|
+
├── App.swift # @main entry point
|
|
87
|
+
├── AppDelegate.swift # UIKit lifecycle (if needed)
|
|
88
|
+
├── Info.plist
|
|
89
|
+
├── Assets.xcassets/
|
|
90
|
+
├── DI/
|
|
91
|
+
│ └── AppContainer.swift
|
|
92
|
+
├── Data/
|
|
93
|
+
│ ├── Network/
|
|
94
|
+
│ │ ├── APIClient.swift
|
|
95
|
+
│ │ ├── Endpoints/
|
|
96
|
+
│ │ └── DTOs/
|
|
97
|
+
│ ├── Local/
|
|
98
|
+
│ │ ├── SwiftDataModels/
|
|
99
|
+
│ │ ├── KeychainService.swift
|
|
100
|
+
│ │ └── UserDefaultsKeys.swift
|
|
101
|
+
│ └── Repositories/
|
|
102
|
+
├── Domain/
|
|
103
|
+
│ ├── Models/
|
|
104
|
+
│ ├── Repositories/
|
|
105
|
+
│ └── UseCases/
|
|
106
|
+
├── Presentation/
|
|
107
|
+
│ ├── Navigation/
|
|
108
|
+
│ │ ├── AppNavigation.swift
|
|
109
|
+
│ │ └── Route.swift
|
|
110
|
+
│ ├── Theme/
|
|
111
|
+
│ │ ├── AppTheme.swift
|
|
112
|
+
│ │ └── Components/
|
|
113
|
+
│ └── Screens/
|
|
114
|
+
│ ├── Launch/
|
|
115
|
+
│ ├── Auth/
|
|
116
|
+
│ ├── Home/
|
|
117
|
+
│ └── .../
|
|
118
|
+
├── Utilities/
|
|
119
|
+
│ ├── Extensions/
|
|
120
|
+
│ ├── Crypto/
|
|
121
|
+
│ └── Helpers/
|
|
122
|
+
└── Resources/
|
|
123
|
+
├── Localizable.xcstrings
|
|
124
|
+
└── Fonts/
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 1.6: Build Order
|
|
128
|
+
|
|
129
|
+
| # | Phase | Scope | Complexity |
|
|
130
|
+
|---|-------|-------|-----------|
|
|
131
|
+
| 1 | 🟢 Setup | Xcode project + SPM deps + Theme | Low |
|
|
132
|
+
| 2 | 🟢 Models | Domain structs | Low |
|
|
133
|
+
| 3 | 🟡 Data | APIClient + SwiftData + Keychain | Medium |
|
|
134
|
+
| 4 | 🟡 Utils | Crypto, formatters (parity test!) | Medium |
|
|
135
|
+
| 5 | 🔴 Feature: [First] | Blueprint → Build | High |
|
|
136
|
+
| 6 | 🔴 Feature: [Second] | Blueprint → Build | High |
|
|
137
|
+
| N | 🔴 Final | Parity check + QA | High |
|
|
138
|
+
|
|
139
|
+
### 1.7: Tech Stack Decisions
|
|
140
|
+
|
|
141
|
+
| Decision | Choice | Rationale |
|
|
142
|
+
|----------|--------|-----------|
|
|
143
|
+
| UI | SwiftUI + iOS 17+ | Modern, declarative |
|
|
144
|
+
| State | @Observable | Latest, no Combine boilerplate |
|
|
145
|
+
| Network | URLSession async/await | Apple native, no deps |
|
|
146
|
+
| JSON | Codable | Built-in, type-safe |
|
|
147
|
+
| Local DB | SwiftData | [or Core Data if < iOS 17] |
|
|
148
|
+
| DI | Protocol + init injection | No framework needed |
|
|
149
|
+
| Package Manager | SPM | Standard, no CocoaPods |
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## 📊 Output: Architecture Blueprint
|
|
154
|
+
|
|
155
|
+
Use template from `templates/architecture.md`.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## ✅ Gate
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
"🏗️ Architecture Blueprint xong. Anh muốn bắt đầu từ feature nào?"
|
|
163
|
+
→ User picks feature → Phase 2 (Blueprint)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
*Phase 1: Architecture — Design before you build*
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
# 📐 Phase 2: Blueprint + UI Design (Per Feature) — iOS
|
|
2
|
+
|
|
3
|
+
> **Zoom Level:** 2 — Feature Detail
|
|
4
|
+
> **Goal:** Design contracts AND code UI visual shell for ONE feature.
|
|
5
|
+
> **Input:** Architecture Blueprint (Phase 1) + user's chosen feature.
|
|
6
|
+
> **Output:** Approved contracts + Working SwiftUI shell with mock data.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## ⛔ OUTPUT RULE
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
PART A — Contracts (signatures only):
|
|
14
|
+
✅ Protocol definitions, struct/enum types
|
|
15
|
+
✅ APIClient endpoint definitions (signature only, no body)
|
|
16
|
+
✅ ViewState struct, ViewAction enum
|
|
17
|
+
❌ Function body implementations (use fatalError("TODO"))
|
|
18
|
+
|
|
19
|
+
PART B — UI (visual code):
|
|
20
|
+
✅ Full SwiftUI code for screen
|
|
21
|
+
✅ Hardcoded mock data (using ViewState from Part A)
|
|
22
|
+
✅ Real resources (icons, colors, images extracted in 2.7)
|
|
23
|
+
✅ #Preview for ALL states (normal, loading, error, empty)
|
|
24
|
+
❌ Real ViewModel connection — use parameter defaults
|
|
25
|
+
❌ Real API calls
|
|
26
|
+
❌ Business logic
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 📋 Sub-steps
|
|
32
|
+
|
|
33
|
+
### 2.1: Deep Source Reading
|
|
34
|
+
|
|
35
|
+
Read class-dump headers + Hopper pseudo-code for the chosen feature.
|
|
36
|
+
|
|
37
|
+
**What to extract:**
|
|
38
|
+
- Class hierarchy (@interface, @protocol)
|
|
39
|
+
- Property declarations → model fields
|
|
40
|
+
- Method signatures → API contracts
|
|
41
|
+
- String constants → URLs, keys
|
|
42
|
+
- Delegate/callback patterns → async contract signatures
|
|
43
|
+
|
|
44
|
+
**ObjC Header Quick Ref:**
|
|
45
|
+
```objc
|
|
46
|
+
@interface ClassName : SuperClass <Protocol1, Protocol2>
|
|
47
|
+
@property (nonatomic, copy) NSString *name; // → let name: String
|
|
48
|
+
@property (nonatomic, strong) NSArray *items; // → let items: [Item]
|
|
49
|
+
@property (nonatomic, assign) BOOL isActive; // → let isActive: Bool
|
|
50
|
+
@property (nullable, nonatomic, copy) NSString *bio; // → let bio: String?
|
|
51
|
+
- (void)fetchDataWithCompletion:(void (^)(id, NSError *))completion;
|
|
52
|
+
// → func fetchData() async throws -> Data
|
|
53
|
+
@end
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
See `objc-reading-guide.md` for comprehensive guide.
|
|
57
|
+
|
|
58
|
+
### 2.2: Domain Model Contracts
|
|
59
|
+
|
|
60
|
+
```swift
|
|
61
|
+
// Domain model
|
|
62
|
+
struct User: Identifiable, Sendable {
|
|
63
|
+
let id: String
|
|
64
|
+
let fullName: String
|
|
65
|
+
let email: String
|
|
66
|
+
let avatarURL: URL?
|
|
67
|
+
let isVerified: Bool
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// DTO (from API)
|
|
71
|
+
struct UserDTO: Codable {
|
|
72
|
+
let userId: String
|
|
73
|
+
let fullName: String
|
|
74
|
+
let email: String
|
|
75
|
+
let avatarUrl: String?
|
|
76
|
+
let isVerified: Bool
|
|
77
|
+
|
|
78
|
+
enum CodingKeys: String, CodingKey {
|
|
79
|
+
case userId = "user_id"
|
|
80
|
+
case fullName = "full_name"
|
|
81
|
+
case email
|
|
82
|
+
case avatarUrl = "avatar_url"
|
|
83
|
+
case isVerified = "is_verified"
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
func toDomain() -> User {
|
|
87
|
+
User(id: userId, fullName: fullName, email: email,
|
|
88
|
+
avatarURL: avatarUrl.flatMap(URL.init), isVerified: isVerified)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 2.3: Repository Contract
|
|
94
|
+
|
|
95
|
+
```swift
|
|
96
|
+
protocol AuthRepository: Sendable {
|
|
97
|
+
func login(email: String, password: String) async throws -> User
|
|
98
|
+
func register(name: String, email: String, password: String) async throws -> User
|
|
99
|
+
func logout() async
|
|
100
|
+
func isLoggedIn() -> Bool
|
|
101
|
+
func currentUser() async -> User?
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 2.4: API Contract
|
|
106
|
+
|
|
107
|
+
```swift
|
|
108
|
+
enum AuthEndpoint {
|
|
109
|
+
case login(email: String, password: String)
|
|
110
|
+
case register(name: String, email: String, password: String)
|
|
111
|
+
case currentUser
|
|
112
|
+
|
|
113
|
+
var path: String { /* ... */ }
|
|
114
|
+
var method: HTTPMethod { /* ... */ }
|
|
115
|
+
// Signature only, no URLRequest body yet
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 2.5: UseCase Signatures
|
|
120
|
+
|
|
121
|
+
```swift
|
|
122
|
+
struct LoginUseCase {
|
|
123
|
+
private let authRepo: AuthRepository
|
|
124
|
+
|
|
125
|
+
func execute(email: String, password: String) async throws -> User {
|
|
126
|
+
fatalError("TODO: implement in Phase 3")
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### 2.6: UI State Design
|
|
132
|
+
|
|
133
|
+
```swift
|
|
134
|
+
// State
|
|
135
|
+
struct LoginViewState {
|
|
136
|
+
var email: String = ""
|
|
137
|
+
var password: String = ""
|
|
138
|
+
var isLoading: Bool = false
|
|
139
|
+
var error: String? = nil
|
|
140
|
+
var isPasswordVisible: Bool = false
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Actions (user interactions)
|
|
144
|
+
enum LoginAction {
|
|
145
|
+
case updateEmail(String)
|
|
146
|
+
case updatePassword(String)
|
|
147
|
+
case togglePasswordVisibility
|
|
148
|
+
case submit
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Events (one-time navigation/alerts)
|
|
152
|
+
enum LoginEvent {
|
|
153
|
+
case navigateToHome
|
|
154
|
+
case showError(String)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Mock data for Preview
|
|
158
|
+
extension LoginViewState {
|
|
159
|
+
static let normal = LoginViewState(email: "user@example.com")
|
|
160
|
+
static let loading = LoginViewState(isLoading: true)
|
|
161
|
+
static let error = LoginViewState(error: "Invalid credentials")
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 2.7: Resource Extraction ⭐ (Before UI code!)
|
|
166
|
+
|
|
167
|
+
> **Why here?** UI code (2.8) needs real assets. If resources come later, UI will be full of placeholders — defeating the purpose of visual parity.
|
|
168
|
+
|
|
169
|
+
**Process:**
|
|
170
|
+
1. Extract from `Assets.car` (use `acextract` or screen captures)
|
|
171
|
+
2. Extract from IPA bundle: png, pdf, json, fonts
|
|
172
|
+
3. Copy ONLY needed resources to new Xcode project `Assets.xcassets`
|
|
173
|
+
4. Map string tables to `Localizable.xcstrings`
|
|
174
|
+
|
|
175
|
+
**Output:**
|
|
176
|
+
```markdown
|
|
177
|
+
### 📦 Resources for [Screen]
|
|
178
|
+
- Images: [list from Assets.xcassets]
|
|
179
|
+
- Colors: [list — add to Color extension or asset catalog]
|
|
180
|
+
- Strings: [list — add to Localizable.xcstrings]
|
|
181
|
+
- Fonts: [custom fonts if any]
|
|
182
|
+
✅ All accessible in SwiftUI
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### 2.8: UI Implementation — Visual Shell ⭐
|
|
186
|
+
|
|
187
|
+
> **Goal:** Code the screen with HARDCODED mock data. Pixel-perfect visual parity with original app.
|
|
188
|
+
|
|
189
|
+
**Rules:**
|
|
190
|
+
```
|
|
191
|
+
✅ MUST DO:
|
|
192
|
+
- Use ViewState from 2.6 (hardcode sample values as defaults)
|
|
193
|
+
- Use REAL resources extracted in 2.7
|
|
194
|
+
- Display ALL visual elements from original app
|
|
195
|
+
- Match: spacing, font size, colors, icon placement
|
|
196
|
+
- Code ALL states: normal, loading, error, empty
|
|
197
|
+
- Create #Preview for each state
|
|
198
|
+
|
|
199
|
+
❌ MUST NOT:
|
|
200
|
+
- Connect real ViewModel (use parameter defaults)
|
|
201
|
+
- Call real APIs
|
|
202
|
+
- Code business logic
|
|
203
|
+
- Use DI/injection
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**Pattern — Stateless View:**
|
|
207
|
+
```swift
|
|
208
|
+
struct LoginScreenContent: View {
|
|
209
|
+
var state: LoginViewState = .normal // Hardcoded default
|
|
210
|
+
var onAction: (LoginAction) -> Void = { _ in } // No-op default
|
|
211
|
+
|
|
212
|
+
var body: some View {
|
|
213
|
+
VStack(spacing: 24) {
|
|
214
|
+
// Logo — real resource from 2.7
|
|
215
|
+
Image("app_logo")
|
|
216
|
+
.resizable()
|
|
217
|
+
.frame(width: 120, height: 120)
|
|
218
|
+
|
|
219
|
+
Spacer().frame(height: 24)
|
|
220
|
+
|
|
221
|
+
// Email
|
|
222
|
+
TextField("Email", text: .constant(state.email))
|
|
223
|
+
.textFieldStyle(.roundedBorder)
|
|
224
|
+
.keyboardType(.emailAddress)
|
|
225
|
+
.textContentType(.emailAddress)
|
|
226
|
+
|
|
227
|
+
// Password with toggle
|
|
228
|
+
HStack {
|
|
229
|
+
if state.isPasswordVisible {
|
|
230
|
+
TextField("Password", text: .constant(state.password))
|
|
231
|
+
} else {
|
|
232
|
+
SecureField("Password", text: .constant(state.password))
|
|
233
|
+
}
|
|
234
|
+
Button { onAction(.togglePasswordVisibility) } label: {
|
|
235
|
+
Image(systemName: state.isPasswordVisible ? "eye.slash" : "eye")
|
|
236
|
+
.foregroundColor(.secondary)
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
.textFieldStyle(.roundedBorder)
|
|
240
|
+
|
|
241
|
+
// Login Button
|
|
242
|
+
Button { onAction(.submit) } label: {
|
|
243
|
+
if state.isLoading {
|
|
244
|
+
ProgressView()
|
|
245
|
+
.tint(.white)
|
|
246
|
+
} else {
|
|
247
|
+
Text("Login")
|
|
248
|
+
.fontWeight(.semibold)
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
.frame(maxWidth: .infinity, minHeight: 50)
|
|
252
|
+
.background(Color.accentColor)
|
|
253
|
+
.foregroundColor(.white)
|
|
254
|
+
.cornerRadius(12)
|
|
255
|
+
.disabled(state.isLoading)
|
|
256
|
+
|
|
257
|
+
// Error
|
|
258
|
+
if let error = state.error {
|
|
259
|
+
Text(error)
|
|
260
|
+
.foregroundColor(.red)
|
|
261
|
+
.font(.caption)
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
.padding(24)
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ===== PREVIEWS =====
|
|
269
|
+
#Preview("Normal") {
|
|
270
|
+
LoginScreenContent(state: .normal)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
#Preview("Loading") {
|
|
274
|
+
LoginScreenContent(state: .loading)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
#Preview("Error") {
|
|
278
|
+
LoginScreenContent(state: .error)
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### 2.9: Visual Parity Check ⭐
|
|
283
|
+
|
|
284
|
+
Compare UI shell with original app screenshot:
|
|
285
|
+
|
|
286
|
+
```markdown
|
|
287
|
+
### 📸 Visual Parity: [Screen Name]
|
|
288
|
+
|
|
289
|
+
Layout:
|
|
290
|
+
- [ ] Structure matches original (components, sections)
|
|
291
|
+
- [ ] Spacing between elements correct
|
|
292
|
+
- [ ] Alignment matches
|
|
293
|
+
|
|
294
|
+
Styling:
|
|
295
|
+
- [ ] Colors match (background, text, buttons, header)
|
|
296
|
+
- [ ] Typography matches (font size, weight)
|
|
297
|
+
- [ ] Icons/images correct and positioned
|
|
298
|
+
- [ ] Borders, shadows, rounded corners
|
|
299
|
+
|
|
300
|
+
States:
|
|
301
|
+
- [ ] Normal state displays correctly
|
|
302
|
+
- [ ] Loading state — progress in right position
|
|
303
|
+
- [ ] Error state — error message shows properly
|
|
304
|
+
- [ ] Empty state — guidance shown
|
|
305
|
+
|
|
306
|
+
Platform:
|
|
307
|
+
- [ ] iOS conventions followed (safe area, etc.)
|
|
308
|
+
- [ ] Dynamic Type support
|
|
309
|
+
- [ ] Looks good on different iPhone sizes
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## 📊 Output: Feature Blueprint + UI
|
|
315
|
+
|
|
316
|
+
Structured document with contracts + screen screenshots.
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## ✅ Gate (MANDATORY)
|
|
321
|
+
|
|
322
|
+
```
|
|
323
|
+
"📐 Blueprint + UI cho [Feature] xong:
|
|
324
|
+
|
|
325
|
+
📝 Contracts:
|
|
326
|
+
- [N] domain models
|
|
327
|
+
- [N] repository protocols
|
|
328
|
+
- [N] use case signatures
|
|
329
|
+
- [N] API endpoints
|
|
330
|
+
|
|
331
|
+
🎨 UI:
|
|
332
|
+
- [Screen] implemented with mock data
|
|
333
|
+
- Resources: [N] images, [N] strings, [N] colors
|
|
334
|
+
- Previews: [N] states (normal, loading, error, empty)
|
|
335
|
+
|
|
336
|
+
📸 Visual Parity: [OK / cần chỉnh X, Y, Z]
|
|
337
|
+
|
|
338
|
+
Anh xem UI + contracts ổn không?
|
|
339
|
+
→ ✅ Approve → Phase 3 (Logic Build)
|
|
340
|
+
→ 🎨 Adjust UI → fix then re-check
|
|
341
|
+
→ 📝 Adjust contracts → revise blueprint"
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
> ⚠️ **DO NOT proceed to Phase 3 until user approves BOTH UI and contracts.**
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
*Phase 2: Blueprint + UI Design — See it before you code it*
|