@buivietphi/skill-mobile-mt 1.0.0
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/AGENTS.md +392 -0
- package/README.md +224 -0
- package/SKILL.md +1048 -0
- package/android/android-native.md +208 -0
- package/bin/install.mjs +199 -0
- package/flutter/flutter.md +246 -0
- package/ios/ios-native.md +182 -0
- package/package.json +50 -0
- package/react-native/react-native.md +743 -0
- package/shared/agent-rules-template.md +343 -0
- package/shared/anti-patterns.md +407 -0
- package/shared/bug-detection.md +71 -0
- package/shared/claude-md-template.md +125 -0
- package/shared/code-review.md +121 -0
- package/shared/common-pitfalls.md +117 -0
- package/shared/document-analysis.md +167 -0
- package/shared/error-recovery.md +467 -0
- package/shared/observability.md +688 -0
- package/shared/performance-prediction.md +210 -0
- package/shared/platform-excellence.md +159 -0
- package/shared/prompt-engineering.md +677 -0
- package/shared/release-checklist.md +82 -0
- package/shared/version-management.md +509 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# iOS Native — Production Patterns
|
|
2
|
+
|
|
3
|
+
> Battle-tested patterns for iOS Swift development.
|
|
4
|
+
> Also used as reference for RN/Flutter iOS native module issues.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Clean Architecture
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
ProjectName/
|
|
12
|
+
├── ProjectName.xcodeproj
|
|
13
|
+
├── ProjectName.xcworkspace # CocoaPods workspace
|
|
14
|
+
├── Podfile # CocoaPods deps
|
|
15
|
+
├── Gemfile # Ruby deps for CocoaPods
|
|
16
|
+
├── ProjectName/
|
|
17
|
+
│ ├── App/
|
|
18
|
+
│ │ ├── AppDelegate.swift # Or @main App struct
|
|
19
|
+
│ │ └── SceneDelegate.swift
|
|
20
|
+
│ ├── Domain/ # Business logic (pure)
|
|
21
|
+
│ │ ├── Entities/ # Core business models
|
|
22
|
+
│ │ ├── UseCases/ # Business rules
|
|
23
|
+
│ │ └── Repositories/ # Repository protocols (contracts)
|
|
24
|
+
│ ├── Data/ # Data layer (implements Domain)
|
|
25
|
+
│ │ ├── Repositories/ # Repository implementations
|
|
26
|
+
│ │ ├── Network/ # APIClient, endpoints
|
|
27
|
+
│ │ ├── Storage/ # KeychainManager, CoreData
|
|
28
|
+
│ │ └── DTOs/ # Data transfer objects + mappers
|
|
29
|
+
│ ├── Presentation/ # UI layer
|
|
30
|
+
│ │ ├── Features/
|
|
31
|
+
│ │ │ ├── Auth/
|
|
32
|
+
│ │ │ │ ├── Views/
|
|
33
|
+
│ │ │ │ └── ViewModels/
|
|
34
|
+
│ │ │ └── [Feature]/
|
|
35
|
+
│ │ └── Shared/
|
|
36
|
+
│ │ ├── Components/
|
|
37
|
+
│ │ └── Utils/
|
|
38
|
+
│ └── Resources/
|
|
39
|
+
│ ├── Assets.xcassets
|
|
40
|
+
│ └── Info.plist
|
|
41
|
+
└── Pods/ # CocoaPods output
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Dependency Rule
|
|
45
|
+
```
|
|
46
|
+
Presentation/ → Domain/ ← Data/
|
|
47
|
+
|
|
48
|
+
Views and ViewModels depend on Domain.
|
|
49
|
+
Data layer implements Domain protocols.
|
|
50
|
+
Domain depends on NOTHING.
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## SwiftUI View + ViewModel (MVVM)
|
|
54
|
+
|
|
55
|
+
```swift
|
|
56
|
+
struct ProductListView: View {
|
|
57
|
+
@StateObject private var viewModel = ProductListViewModel()
|
|
58
|
+
|
|
59
|
+
var body: some View {
|
|
60
|
+
NavigationStack {
|
|
61
|
+
content
|
|
62
|
+
.navigationTitle("Products")
|
|
63
|
+
.refreshable { await viewModel.refresh() }
|
|
64
|
+
}
|
|
65
|
+
.task { await viewModel.loadIfNeeded() }
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@ViewBuilder
|
|
69
|
+
private var content: some View {
|
|
70
|
+
switch viewModel.state {
|
|
71
|
+
case .loading: ProgressView()
|
|
72
|
+
case .loaded(let items) where items.isEmpty:
|
|
73
|
+
ContentUnavailableView("No Items", systemImage: "tray")
|
|
74
|
+
case .loaded(let items):
|
|
75
|
+
List(items) { item in
|
|
76
|
+
NavigationLink(value: item) { ItemRow(item: item) }
|
|
77
|
+
}
|
|
78
|
+
case .error(let error):
|
|
79
|
+
ContentUnavailableView {
|
|
80
|
+
Label("Error", systemImage: "exclamationmark.triangle")
|
|
81
|
+
} description: { Text(error.localizedDescription) }
|
|
82
|
+
actions: { Button("Retry") { Task { await viewModel.refresh() } } }
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@MainActor
|
|
88
|
+
final class ProductListViewModel: ObservableObject {
|
|
89
|
+
enum State { case loading, loaded([Product]), error(Error) }
|
|
90
|
+
@Published private(set) var state: State = .loading
|
|
91
|
+
|
|
92
|
+
private let useCase: GetProductsUseCase
|
|
93
|
+
init(useCase: GetProductsUseCase = GetProductsUseCaseImpl()) { self.useCase = useCase }
|
|
94
|
+
|
|
95
|
+
func loadIfNeeded() async {
|
|
96
|
+
guard case .loading = state else { return }
|
|
97
|
+
await refresh()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
func refresh() async {
|
|
101
|
+
state = .loading
|
|
102
|
+
do { state = .loaded(try await useCase.execute()) }
|
|
103
|
+
catch { state = .error(error) }
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Networking
|
|
109
|
+
|
|
110
|
+
```swift
|
|
111
|
+
actor APIClient {
|
|
112
|
+
func request<T: Decodable>(_ endpoint: Endpoint, as: T.Type) async throws -> T {
|
|
113
|
+
var req = endpoint.urlRequest(baseURL: baseURL)
|
|
114
|
+
if endpoint.requiresAuth {
|
|
115
|
+
req.setValue("Bearer \(try await tokenProvider.token())", forHTTPHeaderField: "Authorization")
|
|
116
|
+
}
|
|
117
|
+
let (data, response) = try await URLSession.shared.data(for: req)
|
|
118
|
+
guard let http = response as? HTTPURLResponse, (200...299).contains(http.statusCode) else {
|
|
119
|
+
throw APIError.from(response: response as? HTTPURLResponse)
|
|
120
|
+
}
|
|
121
|
+
return try JSONDecoder().decode(T.self, from: data)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Keychain Storage
|
|
127
|
+
|
|
128
|
+
```swift
|
|
129
|
+
final class KeychainManager {
|
|
130
|
+
static let shared = KeychainManager()
|
|
131
|
+
func save(_ data: Data, for key: String) throws {
|
|
132
|
+
let query: [CFString: Any] = [
|
|
133
|
+
kSecClass: kSecClassGenericPassword, kSecAttrAccount: key,
|
|
134
|
+
kSecValueData: data, kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
|
|
135
|
+
]
|
|
136
|
+
SecItemDelete(query as CFDictionary)
|
|
137
|
+
guard SecItemAdd(query as CFDictionary, nil) == errSecSuccess else { throw KeychainError.saveFailed }
|
|
138
|
+
}
|
|
139
|
+
func load(for key: String) -> Data? {
|
|
140
|
+
let query: [CFString: Any] = [
|
|
141
|
+
kSecClass: kSecClassGenericPassword, kSecAttrAccount: key,
|
|
142
|
+
kSecReturnData: true, kSecMatchLimit: kSecMatchLimitOne,
|
|
143
|
+
]
|
|
144
|
+
var result: AnyObject?
|
|
145
|
+
return SecItemCopyMatching(query as CFDictionary, &result) == errSecSuccess ? result as? Data : nil
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## CocoaPods
|
|
151
|
+
|
|
152
|
+
```ruby
|
|
153
|
+
# Podfile
|
|
154
|
+
platform :ios, '15.0'
|
|
155
|
+
use_frameworks!
|
|
156
|
+
|
|
157
|
+
target 'ProjectName' do
|
|
158
|
+
pod 'Alamofire'
|
|
159
|
+
pod 'Kingfisher'
|
|
160
|
+
pod 'SnapKit'
|
|
161
|
+
end
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
cd ios && pod install
|
|
166
|
+
bundle exec pod install # If using Bundler
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Common Pitfalls
|
|
170
|
+
|
|
171
|
+
| Pitfall | Fix |
|
|
172
|
+
|---------|-----|
|
|
173
|
+
| Force unwrap `!` | `guard let` / `??` |
|
|
174
|
+
| Retain cycles | `[weak self]` in closures |
|
|
175
|
+
| Main thread violation | `@MainActor` |
|
|
176
|
+
| `NavigationView` deprecated | `NavigationStack` |
|
|
177
|
+
| Hardcoded colors | Semantic: `Color(.label)` |
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
> No force unwraps. No retain cycles. `@MainActor` for all UI state.
|
|
182
|
+
> MVVM + Clean Architecture. Protocols for testability.
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@buivietphi/skill-mobile-mt",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Master Senior Mobile Engineer skill for AI agents. Pre-built patterns from 18 production apps + local project adaptation. React Native, Flutter, iOS, Android. Supports Claude, Gemini, Kimi, Cursor, Copilot, Antigravity.",
|
|
5
|
+
"author": "buivietphi",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"claude-code",
|
|
9
|
+
"gemini",
|
|
10
|
+
"kimi",
|
|
11
|
+
"antigravity",
|
|
12
|
+
"cursor",
|
|
13
|
+
"agent-skills",
|
|
14
|
+
"mobile",
|
|
15
|
+
"react-native",
|
|
16
|
+
"flutter",
|
|
17
|
+
"ios",
|
|
18
|
+
"android",
|
|
19
|
+
"clean-architecture",
|
|
20
|
+
"code-review",
|
|
21
|
+
"prompt-engineering"
|
|
22
|
+
],
|
|
23
|
+
"bin": {
|
|
24
|
+
"skill-mobile": "bin/install.mjs"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"bin/",
|
|
28
|
+
"SKILL.md",
|
|
29
|
+
"AGENTS.md",
|
|
30
|
+
"react-native/",
|
|
31
|
+
"flutter/",
|
|
32
|
+
"ios/",
|
|
33
|
+
"android/",
|
|
34
|
+
"shared/"
|
|
35
|
+
],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"postinstall": "node bin/install.mjs --auto",
|
|
38
|
+
"install:claude": "node bin/install.mjs --claude",
|
|
39
|
+
"install:gemini": "node bin/install.mjs --gemini",
|
|
40
|
+
"install:kimi": "node bin/install.mjs --kimi",
|
|
41
|
+
"install:all": "node bin/install.mjs --all"
|
|
42
|
+
},
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=18"
|
|
45
|
+
},
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "https://github.com/buivietphi/skill-mobile-mt"
|
|
49
|
+
}
|
|
50
|
+
}
|