@buivietphi/skill-mobile-mt 1.0.1 → 1.1.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.
Potentially problematic release.
This version of @buivietphi/skill-mobile-mt might be problematic. Click here for more details.
- package/AGENTS.md +1 -0
- package/SKILL.md +5 -3
- package/bin/install.mjs +2 -2
- package/package.json +1 -1
- package/shared/architecture-intelligence.md +416 -0
package/AGENTS.md
CHANGED
|
@@ -53,6 +53,7 @@ skill-mobile-mt/
|
|
|
53
53
|
├── platform-excellence.md ← iOS 18+ vs Android 15+ UX (1,500 tokens)
|
|
54
54
|
├── version-management.md ← SDK compat matrix + release testing (3,500 tokens)
|
|
55
55
|
├── observability.md ← Sessions as 4th pillar (3,000 tokens)
|
|
56
|
+
├── architecture-intelligence.md ← Patterns from 30+ production repos (4,500 tokens)
|
|
56
57
|
├── common-pitfalls.md ← Known issue patterns (1,160 tokens)
|
|
57
58
|
├── release-checklist.md ← App Store/Play Store checklist (587 tokens)
|
|
58
59
|
│
|
package/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: skill-mobile-mt
|
|
3
|
-
description: "Master Senior Mobile Engineer. Use when: building mobile features, fixing mobile bugs, reviewing mobile code, mobile architecture, React Native, Flutter, iOS Swift, Android Kotlin, mobile performance, mobile security audit, mobile code review, app release. Two modes: (1) default = pre-built production patterns, (2) 'project' = reads current project and adapts."
|
|
4
|
-
version: "1.
|
|
3
|
+
description: "Master Senior Mobile Engineer. Patterns from 30+ production repos (200k+ GitHub stars: Ignite, Expensify, Mattermost, Immich, AppFlowy, Now in Android, TCA). Use when: building mobile features, fixing mobile bugs, reviewing mobile code, mobile architecture, React Native, Flutter, iOS Swift, Android Kotlin, mobile performance, mobile security audit, mobile code review, app release. Two modes: (1) default = pre-built production patterns, (2) 'project' = reads current project and adapts."
|
|
4
|
+
version: "1.1.0"
|
|
5
5
|
author: buivietphi
|
|
6
6
|
priority: high
|
|
7
7
|
user-invocable: true
|
|
@@ -72,7 +72,9 @@ USER REQUEST → ACTION (Read tool required)
|
|
|
72
72
|
|
|
73
73
|
"Add X to existing Y" → MODIFY existing files, don't create new structure
|
|
74
74
|
|
|
75
|
-
"Setup project / architecture" → Read
|
|
75
|
+
"Setup project / architecture" → Read: shared/architecture-intelligence.md
|
|
76
|
+
then: Read platform file (see Smart Loading below)
|
|
77
|
+
then: suggest structure based on project size + stack
|
|
76
78
|
|
|
77
79
|
"Fix / debug X" → Read: shared/bug-detection.md
|
|
78
80
|
then: read code → find root cause → fix → verify
|
package/bin/install.mjs
CHANGED
|
@@ -34,7 +34,7 @@ const SUBFOLDERS = {
|
|
|
34
34
|
'flutter': ['flutter.md'],
|
|
35
35
|
'ios': ['ios-native.md'],
|
|
36
36
|
'android': ['android-native.md'],
|
|
37
|
-
'shared': ['code-review.md', 'bug-detection.md', 'prompt-engineering.md', 'release-checklist.md', 'common-pitfalls.md', 'error-recovery.md', 'document-analysis.md', 'anti-patterns.md', 'performance-prediction.md', 'platform-excellence.md', 'version-management.md', 'observability.md', 'claude-md-template.md', 'agent-rules-template.md'],
|
|
37
|
+
'shared': ['code-review.md', 'bug-detection.md', 'prompt-engineering.md', 'release-checklist.md', 'common-pitfalls.md', 'error-recovery.md', 'document-analysis.md', 'anti-patterns.md', 'performance-prediction.md', 'platform-excellence.md', 'version-management.md', 'observability.md', 'architecture-intelligence.md', 'claude-md-template.md', 'agent-rules-template.md'],
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
const AGENTS = {
|
|
@@ -56,7 +56,7 @@ const fail = m => log(` ${c.red}✗${c.reset} ${m}`);
|
|
|
56
56
|
|
|
57
57
|
function banner() {
|
|
58
58
|
log(`\n${c.bold}${c.cyan} ┌──────────────────────────────────────────────┐`);
|
|
59
|
-
log(` │ 📱 @buivietphi/skill-mobile-mt v1.0
|
|
59
|
+
log(` │ 📱 @buivietphi/skill-mobile-mt v1.1.0 │`);
|
|
60
60
|
log(` │ Master Senior Mobile Engineer │`);
|
|
61
61
|
log(` │ │`);
|
|
62
62
|
log(` │ Claude · Codex · Gemini · Kimi │`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buivietphi/skill-mobile-mt",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
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
5
|
"author": "buivietphi",
|
|
6
6
|
"license": "MIT",
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
# Architecture Intelligence — Patterns from 30+ Production Repos
|
|
2
|
+
|
|
3
|
+
> On-demand. Load when: "architecture", "structure", "setup project", "best practices", "how to organize"
|
|
4
|
+
> Source: Analyzed 30+ open-source production mobile apps (total 200k+ GitHub stars)
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Reference Repos (by platform)
|
|
9
|
+
|
|
10
|
+
| Platform | Repo | Stars | Key Pattern |
|
|
11
|
+
|----------|------|-------|-------------|
|
|
12
|
+
| **RN** | Ignite (infinitered) | 19.7k | MST + MMKV + generators |
|
|
13
|
+
| **RN** | Obytes Template | 4k | Zustand + TanStack Query + Expo Router |
|
|
14
|
+
| **RN** | Expensify/App | 4.7k | Onyx custom state + centralized constants |
|
|
15
|
+
| **RN** | Mattermost Mobile | 2.6k | WatermelonDB + offline-first + WebSocket |
|
|
16
|
+
| **RN** | Artsy Eigen | 3.8k | Relay/GraphQL + Scene pattern |
|
|
17
|
+
| **Flutter** | Immich | 93.5k | Riverpod + drift + auto_route + clean arch |
|
|
18
|
+
| **Flutter** | AppFlowy | 68.2k | BLoC + GetIt + startup/ pattern |
|
|
19
|
+
| **Flutter** | Spotube | 44.6k | Riverpod + Hooks + custom hooks folder |
|
|
20
|
+
| **Flutter** | Hiddify | 26.7k | Riverpod + go_router + bootstrap.dart |
|
|
21
|
+
| **Flutter** | Ente Photos | 24.8k | Melos monorepo + gateway pattern |
|
|
22
|
+
| **iOS** | TCA (Point-Free) | 14.4k | Unidirectional + TestStore + @Dependency |
|
|
23
|
+
| **iOS** | Clean Arch SwiftUI | 6.5k | Redux state + @Environment DI |
|
|
24
|
+
| **iOS** | Modern Clean Arch | 4.1k | Tuist + 5-layer DDD + MVVM+TCA coexist |
|
|
25
|
+
| **Android** | Now in Android | 20.7k | Official Google arch + no-mock testing |
|
|
26
|
+
| **Android** | Android Showcase | 6.7k | Konsist validation + Koin DI |
|
|
27
|
+
| **Android** | Mihon | 18.8k | Plugin/extension arch + MVI |
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Cross-Platform Architecture Patterns
|
|
32
|
+
|
|
33
|
+
### 1. Dual State Management (Client + Server)
|
|
34
|
+
|
|
35
|
+
**The pattern:** Separate client state (UI, forms) from server state (API cache).
|
|
36
|
+
|
|
37
|
+
| Platform | Client State | Server State |
|
|
38
|
+
|----------|-------------|--------------|
|
|
39
|
+
| React Native | Zustand / MST | TanStack Query |
|
|
40
|
+
| Flutter | Riverpod | Riverpod + drift |
|
|
41
|
+
| iOS | @Observable / TCA | URLSession cache |
|
|
42
|
+
| Android | ViewModel + Flow | Repository + Room |
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
// RN: Zustand for client + TanStack Query for server
|
|
46
|
+
const useAuthStore = create((set) => ({
|
|
47
|
+
token: null,
|
|
48
|
+
setToken: (t) => set({ token: t }),
|
|
49
|
+
}));
|
|
50
|
+
|
|
51
|
+
const { data, isLoading } = useQuery({
|
|
52
|
+
queryKey: ['products'],
|
|
53
|
+
queryFn: () => api.getProducts(),
|
|
54
|
+
staleTime: 5 * 60 * 1000,
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```dart
|
|
59
|
+
// Flutter: Riverpod for both, but separated
|
|
60
|
+
@riverpod
|
|
61
|
+
class AuthNotifier extends _$AuthNotifier { ... } // client
|
|
62
|
+
|
|
63
|
+
@riverpod
|
|
64
|
+
Future<List<Product>> products(Ref ref) async { ... } // server
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 2. Feature-Based Module Organization
|
|
68
|
+
|
|
69
|
+
**Every top repo (100%) uses feature-based organization, NOT type-based.**
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
// ✅ CORRECT: Feature-based (Immich, Obytes, Hiddify, Now in Android)
|
|
73
|
+
src/features/
|
|
74
|
+
auth/
|
|
75
|
+
domain/ # entities, use cases, repo interfaces
|
|
76
|
+
data/ # repo impl, API, DTOs, mappers
|
|
77
|
+
presentation/ # screens, widgets, viewmodels
|
|
78
|
+
|
|
79
|
+
// ❌ WRONG: Type-based (no production app uses this)
|
|
80
|
+
src/
|
|
81
|
+
models/
|
|
82
|
+
services/
|
|
83
|
+
screens/
|
|
84
|
+
widgets/
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 3. Centralized Constants Pattern (Expensify)
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
// src/ROUTES.ts — Prevents string duplication
|
|
91
|
+
const ROUTES = {
|
|
92
|
+
HOME: 'home',
|
|
93
|
+
SETTINGS: 'settings',
|
|
94
|
+
PROFILE: 'profile/:id',
|
|
95
|
+
} as const;
|
|
96
|
+
|
|
97
|
+
// src/SCREENS.ts — Screen component names
|
|
98
|
+
const SCREENS = {
|
|
99
|
+
HOME: 'HomeScreen',
|
|
100
|
+
SETTINGS: 'SettingsScreen',
|
|
101
|
+
} as const;
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 4. Bootstrap / Startup Pattern (Hiddify, AppFlowy)
|
|
105
|
+
|
|
106
|
+
```dart
|
|
107
|
+
// bootstrap.dart — Clean app initialization
|
|
108
|
+
Future<void> bootstrap() async {
|
|
109
|
+
WidgetsFlutterBinding.ensureInitialized();
|
|
110
|
+
|
|
111
|
+
// 1. Core services
|
|
112
|
+
await Firebase.initializeApp();
|
|
113
|
+
await Hive.initFlutter();
|
|
114
|
+
|
|
115
|
+
// 2. DI registration
|
|
116
|
+
setupServiceLocator();
|
|
117
|
+
|
|
118
|
+
// 3. Run app
|
|
119
|
+
runApp(
|
|
120
|
+
ProviderScope(
|
|
121
|
+
observers: [RiverpodObserver()],
|
|
122
|
+
child: const App(),
|
|
123
|
+
),
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// main.dart
|
|
128
|
+
void main() => bootstrap();
|
|
129
|
+
|
|
130
|
+
// main_prod.dart — Production flavor
|
|
131
|
+
void main() {
|
|
132
|
+
const env = Environment.production;
|
|
133
|
+
bootstrap(env: env);
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### 5. Draft Pairs for Forms (Expensify)
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
// Every form has FORM + FORM_DRAFT for unsaved changes
|
|
141
|
+
const ONYX_KEYS = {
|
|
142
|
+
WORKSPACE_SETTINGS_FORM: 'workspaceSettingsForm',
|
|
143
|
+
WORKSPACE_SETTINGS_FORM_DRAFT: 'workspaceSettingsFormDraft',
|
|
144
|
+
} as const;
|
|
145
|
+
|
|
146
|
+
// Save draft on every keystroke → restore on crash/back
|
|
147
|
+
// Only commit FORM when user explicitly saves
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### 6. Database Subscription Pattern (Mattermost)
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
database/
|
|
154
|
+
models/ # WatermelonDB model definitions
|
|
155
|
+
schema/ # DB structure + versioning
|
|
156
|
+
migration/ # Schema migrations
|
|
157
|
+
operator/ # Complex query logic (keeps models clean)
|
|
158
|
+
subscription/ # Reactive UI updates from DB changes
|
|
159
|
+
exceptions/ # Custom error classes
|
|
160
|
+
manager/ # DB lifecycle (create, reset, destroy)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### 7. Functional Error Handling (Flutter — dartz)
|
|
164
|
+
|
|
165
|
+
```dart
|
|
166
|
+
// Instead of try/catch everywhere, use Either<Failure, Success>
|
|
167
|
+
Future<Either<AppException, User>> getUser(String id) async {
|
|
168
|
+
try {
|
|
169
|
+
final response = await dio.get('/users/$id');
|
|
170
|
+
return Right(User.fromJson(response.data));
|
|
171
|
+
} on DioException catch (e) {
|
|
172
|
+
return Left(NetworkException(e.message));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Usage — forces caller to handle both cases
|
|
177
|
+
final result = await getUser('123');
|
|
178
|
+
result.fold(
|
|
179
|
+
(failure) => showError(failure.message),
|
|
180
|
+
(user) => showProfile(user),
|
|
181
|
+
);
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### 8. Architecture Validation (Android Showcase — Konsist)
|
|
185
|
+
|
|
186
|
+
```kotlin
|
|
187
|
+
// Programmatically enforce architecture rules
|
|
188
|
+
@Test
|
|
189
|
+
fun `domain layer should not depend on data layer`() {
|
|
190
|
+
Konsist.scopeFromModule("feature-album/domain")
|
|
191
|
+
.classes()
|
|
192
|
+
.assertFalse { it.hasImport { import -> import.hasNameContaining("data") } }
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
@Test
|
|
196
|
+
fun `use cases should have 'UseCase' suffix`() {
|
|
197
|
+
Konsist.scopeFromModule("feature-album/domain")
|
|
198
|
+
.classes()
|
|
199
|
+
.withNameContaining("UseCase")
|
|
200
|
+
.assertTrue { it.hasPublicFunction("invoke") }
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Platform-Specific Intelligence
|
|
207
|
+
|
|
208
|
+
### React Native — Must-Know Patterns
|
|
209
|
+
|
|
210
|
+
| Pattern | Old Way | New Way (2024-2025) | Source |
|
|
211
|
+
|---------|---------|---------------------|--------|
|
|
212
|
+
| Storage | AsyncStorage | MMKV (60x faster) | Ignite, Obytes |
|
|
213
|
+
| Routing | React Navigation | Expo Router (file-based) | Obytes |
|
|
214
|
+
| Server state | Redux + thunk | TanStack Query | Obytes, TCM |
|
|
215
|
+
| Client state | Redux | Zustand | Obytes |
|
|
216
|
+
| E2E testing | Detox | Maestro | Ignite, Obytes |
|
|
217
|
+
| Forms | Formik + Yup | TanStack Form + Zod | Obytes |
|
|
218
|
+
| Styling | StyleSheet | NativeWind (TailwindCSS) | Obytes |
|
|
219
|
+
| Animations | Animated API | Reanimated 3 | All |
|
|
220
|
+
| Images | Image | expo-image / FastImage | All |
|
|
221
|
+
|
|
222
|
+
### Flutter — Must-Know Patterns
|
|
223
|
+
|
|
224
|
+
| Pattern | Old Way | New Way (2024-2025) | Source |
|
|
225
|
+
|---------|---------|---------------------|--------|
|
|
226
|
+
| State | setState / Provider | Riverpod + code-gen | Immich, Hiddify, Spotube |
|
|
227
|
+
| State (alt) | BLoC manual | BLoC + freezed | AppFlowy |
|
|
228
|
+
| Navigation | Navigator 2.0 | auto_route / go_router | Immich / Hiddify |
|
|
229
|
+
| Database | sqflite | drift (type-safe ORM) | Immich, Spotube |
|
|
230
|
+
| Models | manual fromJson | freezed + json_serializable | All |
|
|
231
|
+
| i18n | .arb files | slang (type-safe, generated) | Hiddify |
|
|
232
|
+
| HTTP | http package | dio + smart_retry | Hiddify |
|
|
233
|
+
| HTTP (perf) | dio on all | cronet (Android) + cupertino_http (iOS) | Immich |
|
|
234
|
+
| Monorepo | N/A | Melos | Ente |
|
|
235
|
+
| DI | manual | Riverpod providers / GetIt | Immich / AppFlowy |
|
|
236
|
+
|
|
237
|
+
### iOS Swift — Must-Know Patterns
|
|
238
|
+
|
|
239
|
+
| Pattern | Traditional | Modern (2024-2025) | Source |
|
|
240
|
+
|---------|-------------|---------------------|--------|
|
|
241
|
+
| Architecture | MVVM manual | TCA (macro-driven) | Point-Free |
|
|
242
|
+
| DI | Swinject | @Dependency (Point-Free) / @Environment | TCA / nalexn |
|
|
243
|
+
| Testing | XCTest + mocks | TestStore (deterministic) | TCA |
|
|
244
|
+
| Navigation | NavigationView | NavigationStack + Coordinator | sergdort |
|
|
245
|
+
| Modularization | One target | Tuist multi-module | sergdort |
|
|
246
|
+
| Data binding | Combine | @Observable macro | iOS 17+ |
|
|
247
|
+
| SwiftUI testing | None | ViewInspector | nalexn |
|
|
248
|
+
| Concurrency | GCD / Combine | async/await + actors | All |
|
|
249
|
+
|
|
250
|
+
### Android Kotlin — Must-Know Patterns
|
|
251
|
+
|
|
252
|
+
| Pattern | Traditional | Modern (2024-2025) | Source |
|
|
253
|
+
|---------|-------------|---------------------|--------|
|
|
254
|
+
| UI | XML Views | Jetpack Compose | All |
|
|
255
|
+
| Architecture | MVVM | MVVM + UDF (Now in Android) | Google |
|
|
256
|
+
| DI | Dagger 2 | Hilt | Now in Android |
|
|
257
|
+
| DI (lightweight) | N/A | Koin | Android Showcase |
|
|
258
|
+
| Testing | Mockito | No-mock test doubles | Now in Android |
|
|
259
|
+
| Arch validation | Manual review | Konsist | Android Showcase |
|
|
260
|
+
| Performance | ProGuard | Baseline Profiles + R8 | Now in Android |
|
|
261
|
+
| Navigation | Fragment nav | Compose Navigation (type-safe) | All |
|
|
262
|
+
| Build config | build.gradle | Convention Plugins | Now in Android |
|
|
263
|
+
| Screenshot test | None | Roborazzi | Now in Android |
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Production Folder Structure Templates
|
|
268
|
+
|
|
269
|
+
### React Native (Expo Router — based on Obytes)
|
|
270
|
+
|
|
271
|
+
```
|
|
272
|
+
src/
|
|
273
|
+
app/ # Expo Router file-based routes
|
|
274
|
+
(app)/ # Authenticated group
|
|
275
|
+
_layout.tsx
|
|
276
|
+
(tabs)/ # Tab navigator
|
|
277
|
+
login.tsx
|
|
278
|
+
onboarding.tsx
|
|
279
|
+
_layout.tsx # Root layout
|
|
280
|
+
features/ # Feature modules
|
|
281
|
+
auth/
|
|
282
|
+
hooks/
|
|
283
|
+
components/
|
|
284
|
+
services/
|
|
285
|
+
types.ts
|
|
286
|
+
products/
|
|
287
|
+
hooks/
|
|
288
|
+
components/
|
|
289
|
+
services/
|
|
290
|
+
components/
|
|
291
|
+
ui/ # Design system (Button, Input, Card)
|
|
292
|
+
lib/
|
|
293
|
+
api/ # Axios + TanStack Query setup
|
|
294
|
+
auth/ # Token management (Zustand + MMKV)
|
|
295
|
+
hooks/ # Shared hooks
|
|
296
|
+
i18n/ # Internationalization
|
|
297
|
+
storage.ts # MMKV wrapper
|
|
298
|
+
utils.ts # Utilities
|
|
299
|
+
translations/ # Language files
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Flutter (Riverpod + Clean — based on Immich)
|
|
303
|
+
|
|
304
|
+
```
|
|
305
|
+
lib/
|
|
306
|
+
main.dart
|
|
307
|
+
bootstrap.dart # App initialization
|
|
308
|
+
app/
|
|
309
|
+
app.dart # MaterialApp.router
|
|
310
|
+
router.dart # auto_route / go_router config
|
|
311
|
+
theme/
|
|
312
|
+
features/
|
|
313
|
+
auth/
|
|
314
|
+
domain/ # Entities, use cases, repo interfaces
|
|
315
|
+
data/ # Repo impl, datasources, DTOs
|
|
316
|
+
presentation/ # Screens + widgets
|
|
317
|
+
providers/ # Riverpod providers
|
|
318
|
+
[feature]/
|
|
319
|
+
shared/
|
|
320
|
+
widgets/ # Reusable UI
|
|
321
|
+
extensions/ # Dart extensions
|
|
322
|
+
constants/ # App-wide constants
|
|
323
|
+
interfaces/ # Abstract contracts
|
|
324
|
+
models/ # Shared DTOs
|
|
325
|
+
utils/ # Utilities
|
|
326
|
+
l10n/ # Localization
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### iOS SwiftUI (TCA — based on Point-Free + sergdort)
|
|
330
|
+
|
|
331
|
+
```
|
|
332
|
+
App/
|
|
333
|
+
AppDelegate.swift
|
|
334
|
+
AppModule.swift # Composition root
|
|
335
|
+
Features/
|
|
336
|
+
Auth/
|
|
337
|
+
AuthFeature.swift # @Reducer
|
|
338
|
+
AuthView.swift # SwiftUI View
|
|
339
|
+
AuthClient.swift # @Dependency
|
|
340
|
+
Home/
|
|
341
|
+
HomeFeature.swift
|
|
342
|
+
HomeView.swift
|
|
343
|
+
Settings/
|
|
344
|
+
Shared/
|
|
345
|
+
Models/
|
|
346
|
+
Extensions/
|
|
347
|
+
UI/ # Shared SwiftUI components
|
|
348
|
+
Clients/ # API, Storage, Keychain
|
|
349
|
+
Platform/
|
|
350
|
+
Networking/
|
|
351
|
+
Persistence/
|
|
352
|
+
Tests/
|
|
353
|
+
AuthFeatureTests.swift # TestStore tests
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Android Kotlin (Now in Android — based on Google reference)
|
|
357
|
+
|
|
358
|
+
```
|
|
359
|
+
app/ # Main application
|
|
360
|
+
core/
|
|
361
|
+
common/ # Shared utilities
|
|
362
|
+
data/ # Repository implementations
|
|
363
|
+
database/ # Room database
|
|
364
|
+
datastore/ # Proto DataStore
|
|
365
|
+
network/ # Retrofit + OkHttp
|
|
366
|
+
model/ # Core models
|
|
367
|
+
ui/ # Shared Compose components
|
|
368
|
+
testing/ # Test utilities
|
|
369
|
+
feature/
|
|
370
|
+
auth/
|
|
371
|
+
src/main/
|
|
372
|
+
AuthScreen.kt # Compose UI
|
|
373
|
+
AuthViewModel.kt # ViewModel
|
|
374
|
+
AuthUiState.kt # UI state sealed class
|
|
375
|
+
navigation/ # Feature nav graph
|
|
376
|
+
home/
|
|
377
|
+
settings/
|
|
378
|
+
build-logic/ # Convention plugins
|
|
379
|
+
convention/
|
|
380
|
+
AndroidApplicationConventionPlugin.kt
|
|
381
|
+
AndroidFeatureConventionPlugin.kt
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
## Decision Matrix — When to Use What
|
|
387
|
+
|
|
388
|
+
### State Management
|
|
389
|
+
|
|
390
|
+
| Condition | RN | Flutter | iOS | Android |
|
|
391
|
+
|-----------|-----|---------|-----|---------|
|
|
392
|
+
| Simple app (<10 screens) | Zustand | Riverpod | @Observable | ViewModel + StateFlow |
|
|
393
|
+
| Complex app (10-50 screens) | Zustand + TanStack Query | Riverpod + freezed | TCA | MVVM + Hilt + Flow |
|
|
394
|
+
| Enterprise (50+ screens) | Onyx (custom) / Redux Toolkit | BLoC + GetIt | TCA + Tuist | MVVM + Hilt + Convention Plugins |
|
|
395
|
+
| Offline-first | TanStack Query + WatermelonDB | Riverpod + drift | TCA + SwiftData | Room + WorkManager |
|
|
396
|
+
|
|
397
|
+
### Navigation
|
|
398
|
+
|
|
399
|
+
| Condition | RN | Flutter | iOS | Android |
|
|
400
|
+
|-----------|-----|---------|-----|---------|
|
|
401
|
+
| Expo project | Expo Router | — | — | — |
|
|
402
|
+
| RN CLI project | React Navigation | — | — | — |
|
|
403
|
+
| Type-safe routes | — | auto_route | NavigationStack | Compose Navigation |
|
|
404
|
+
| Declarative | Expo Router | go_router | NavigationStack | Compose Navigation |
|
|
405
|
+
| Deep linking | Expo Linking | app_links | Universal Links | App Links |
|
|
406
|
+
|
|
407
|
+
### Testing
|
|
408
|
+
|
|
409
|
+
| What | RN | Flutter | iOS | Android |
|
|
410
|
+
|------|-----|---------|-----|---------|
|
|
411
|
+
| Unit | Jest | test + mocktail | XCTest / TestStore | JUnit + no-mock doubles |
|
|
412
|
+
| Widget/Component | React Testing Library | widget test | ViewInspector | Compose Testing |
|
|
413
|
+
| E2E | Maestro | integration_test | XCUITest | Macrobenchmark |
|
|
414
|
+
| Screenshot | — | golden_toolkit | — | Roborazzi |
|
|
415
|
+
| Architecture | Dependency Cruiser | — | — | Konsist |
|
|
416
|
+
| Performance | Reassure | DevTools | Instruments | Baseline Profiles |
|