mobile-ai-agents 1.0.23
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 +228 -0
- package/CLAUDE.md +236 -0
- package/LICENSE +21 -0
- package/README.md +652 -0
- package/agents/android/android-crash-analyzer/README.md +134 -0
- package/agents/android/android-crash-analyzer/agent.md +206 -0
- package/agents/android/android-crash-analyzer/example-input.md +138 -0
- package/agents/android/android-crash-analyzer/example-output.md +225 -0
- package/agents/android/anr-investigation/README.md +78 -0
- package/agents/android/anr-investigation/agent.md +117 -0
- package/agents/android/anr-investigation/example-input.md +62 -0
- package/agents/android/anr-investigation/example-output.md +103 -0
- package/agents/android/axiom/agent.md +197 -0
- package/agents/android/code-reviewer/README.md +108 -0
- package/agents/android/code-reviewer/agent.md +210 -0
- package/agents/android/code-reviewer/example-input.md +116 -0
- package/agents/android/code-reviewer/example-output.md +115 -0
- package/agents/android/compose-navigation/README.md +76 -0
- package/agents/android/compose-navigation/agent.md +121 -0
- package/agents/android/compose-navigation/example-input.md +49 -0
- package/agents/android/compose-navigation/example-output.md +325 -0
- package/agents/android/compose-screen-builder/README.md +95 -0
- package/agents/android/compose-screen-builder/agent.md +170 -0
- package/agents/android/compose-screen-builder/example-input.md +40 -0
- package/agents/android/compose-screen-builder/example-output.md +568 -0
- package/agents/android/compose-ui-reviewer/README.md +73 -0
- package/agents/android/compose-ui-reviewer/agent.md +253 -0
- package/agents/android/compose-ui-reviewer/example-input.md +84 -0
- package/agents/android/compose-ui-reviewer/example-output.md +102 -0
- package/agents/android/crash-analyzer/README.md +53 -0
- package/agents/android/crash-analyzer/agent.md +295 -0
- package/agents/android/memory-leak-analyzer/README.md +79 -0
- package/agents/android/memory-leak-analyzer/agent.md +116 -0
- package/agents/android/memory-leak-analyzer/example-input.md +78 -0
- package/agents/android/memory-leak-analyzer/example-output.md +128 -0
- package/agents/cross-platform/accessibility-auditor/README.md +47 -0
- package/agents/cross-platform/accessibility-auditor/agent.md +268 -0
- package/agents/cross-platform/accessibility-auditor/example-input.md +84 -0
- package/agents/cross-platform/accessibility-auditor/example-output.md +132 -0
- package/agents/cross-platform/appforge/agent.md +303 -0
- package/agents/cross-platform/ci-cd-generator/README.md +65 -0
- package/agents/cross-platform/ci-cd-generator/agent.md +422 -0
- package/agents/cross-platform/ci-cd-generator/example-input.md +69 -0
- package/agents/cross-platform/ci-cd-generator/example-output.md +261 -0
- package/agents/cross-platform/crasher/agent.md +221 -0
- package/agents/cross-platform/figma/agent.md +247 -0
- package/agents/cross-platform/launchpad/agent.md +330 -0
- package/agents/cross-platform/mobile-harness/agent.md +422 -0
- package/agents/cross-platform/mobile-memory/agent.md +429 -0
- package/agents/cross-platform/perf/agent.md +261 -0
- package/agents/cross-platform/pipeline/agent.md +288 -0
- package/agents/cross-platform/release-notes-generator/README.md +63 -0
- package/agents/cross-platform/release-notes-generator/agent.md +218 -0
- package/agents/cross-platform/release-notes-generator/example-input.md +71 -0
- package/agents/cross-platform/release-notes-generator/example-output.md +96 -0
- package/agents/cross-platform/scribe/agent.md +180 -0
- package/agents/cross-platform/security-scanner/README.md +61 -0
- package/agents/cross-platform/security-scanner/agent.md +287 -0
- package/agents/cross-platform/security-scanner/example-input.md +77 -0
- package/agents/cross-platform/security-scanner/example-output.md +141 -0
- package/agents/cross-platform/sentinel/agent.md +217 -0
- package/agents/cross-platform/store-listing-writer/README.md +58 -0
- package/agents/cross-platform/store-listing-writer/agent.md +380 -0
- package/agents/cross-platform/store-listing-writer/example-input.md +67 -0
- package/agents/cross-platform/store-listing-writer/example-output.md +131 -0
- package/agents/flutter/bloc-feature-builder/README.md +101 -0
- package/agents/flutter/bloc-feature-builder/agent.md +225 -0
- package/agents/flutter/bloc-feature-builder/example-input.md +47 -0
- package/agents/flutter/bloc-feature-builder/example-output.md +838 -0
- package/agents/flutter/dart/agent.md +223 -0
- package/agents/flutter/widget-generator/README.md +66 -0
- package/agents/flutter/widget-generator/agent.md +264 -0
- package/agents/flutter/widget-generator/example-input.md +71 -0
- package/agents/flutter/widget-generator/example-output.md +163 -0
- package/agents/gaming/forge/agent.md +197 -0
- package/agents/gaming/unreal/agent.md +200 -0
- package/agents/ios/crash-analyzer/README.md +69 -0
- package/agents/ios/crash-analyzer/agent.md +300 -0
- package/agents/ios/crash-analyzer/example-input.md +83 -0
- package/agents/ios/crash-analyzer/example-output.md +126 -0
- package/agents/ios/swift/agent.md +206 -0
- package/agents/ios/swift-reviewer/README.md +68 -0
- package/agents/ios/swift-reviewer/agent.md +231 -0
- package/agents/ios/swift-reviewer/example-input.md +100 -0
- package/agents/ios/swift-reviewer/example-output.md +127 -0
- package/agents/react-native/bridge/agent.md +212 -0
- package/agents/react-native/performance-optimizer/README.md +81 -0
- package/agents/react-native/performance-optimizer/agent.md +284 -0
- package/agents/react-native/performance-optimizer/example-input.md +104 -0
- package/agents/react-native/performance-optimizer/example-output.md +132 -0
- package/agents/unity/shader-generator/README.md +77 -0
- package/agents/unity/shader-generator/agent.md +270 -0
- package/agents/unity/shader-generator/example-input.md +70 -0
- package/agents/unity/shader-generator/example-output.md +160 -0
- package/agents/unreal/blueprint-advisor/README.md +71 -0
- package/agents/unreal/blueprint-advisor/agent.md +311 -0
- package/agents/unreal/blueprint-advisor/example-input.md +73 -0
- package/agents/unreal/blueprint-advisor/example-output.md +217 -0
- package/cli/index.js +1091 -0
- package/docs/agent-guide.md +138 -0
- package/docs/chatgpt-integration.md +219 -0
- package/docs/core-loops.md +143 -0
- package/docs/cursor-integration.md +143 -0
- package/docs/getting-started.md +132 -0
- package/docs/index.html +711 -0
- package/docs/installation.md +271 -0
- package/docs/mobile-harness.md +160 -0
- package/docs/mobile-mcp.md +172 -0
- package/docs/mobile-memory.md +349 -0
- package/docs/release-process.md +85 -0
- package/docs/releases/release-v1.0.0.md +165 -0
- package/docs/roadmap/github-issues.md +239 -0
- package/docs/skill-categories.md +125 -0
- package/docs/vscode-copilot-integration.md +104 -0
- package/docs/wiki.md +615 -0
- package/install.sh +507 -0
- package/mobile-karpathy.md +79 -0
- package/package.json +57 -0
- package/skills/android/README.md +31 -0
- package/skills/android/android-tdd.md +89 -0
- package/skills/android/anr-investigation.md +251 -0
- package/skills/android/code-review.md +98 -0
- package/skills/android/compose-migration.md +48 -0
- package/skills/android/compose-review.md +78 -0
- package/skills/android/kotlin-modernize.md +51 -0
- package/skills/android/memory-leak-investigation.md +256 -0
- package/skills/android/proguard-rules.md +48 -0
- package/skills/cross-platform/accessibility-audit.md +63 -0
- package/skills/cross-platform/api-versioning.md +59 -0
- package/skills/cross-platform/clean-code-audit.md +239 -0
- package/skills/cross-platform/crash-triage.md +53 -0
- package/skills/cross-platform/deeplink-debug.md +57 -0
- package/skills/cross-platform/feature-slice.md +48 -0
- package/skills/cross-platform/grill-mobile.md +64 -0
- package/skills/cross-platform/mobile-mcp-qa.md +193 -0
- package/skills/cross-platform/mobile-memory-graph.md +179 -0
- package/skills/cross-platform/mobile-memory-save.md +210 -0
- package/skills/cross-platform/mobile-memory-search.md +154 -0
- package/skills/cross-platform/perf-audit.md +61 -0
- package/skills/cross-platform/prd-verification.md +324 -0
- package/skills/cross-platform/release-prep.md +64 -0
- package/skills/cross-platform/security-audit.md +281 -0
- package/skills/cross-platform/store-listing.md +75 -0
- package/skills/flutter/README.md +32 -0
- package/skills/flutter/dart-modernize.md +47 -0
- package/skills/flutter/flutter-review.md +59 -0
- package/skills/flutter/flutter-tdd.md +73 -0
- package/skills/flutter/widget-extract.md +41 -0
- package/skills/flutter/widget-gen.md +131 -0
- package/skills/gaming/blueprint-to-cpp.md +51 -0
- package/skills/gaming/game-perf.md +54 -0
- package/skills/gaming/shader-gen.md +44 -0
- package/skills/gaming/unity-tdd.md +85 -0
- package/skills/ios/README.md +45 -0
- package/skills/ios/data-persistence.md +151 -0
- package/skills/ios/ios-tdd.md +90 -0
- package/skills/ios/networking.md +139 -0
- package/skills/ios/performance.md +122 -0
- package/skills/ios/swift-concurrency.md +57 -0
- package/skills/ios/swift-review.md +124 -0
- package/skills/ios/swiftui-review.md +49 -0
- package/skills/ios/swiftui-state.md +122 -0
- package/skills/ios/unit-testing.md +144 -0
- package/skills/ios/xcode-warnings.md +37 -0
- package/skills/react-native/README.md +42 -0
- package/skills/react-native/bridge-audit.md +104 -0
- package/skills/react-native/expo-optimize.md +55 -0
- package/skills/react-native/new-arch-migrate.md +53 -0
- package/skills/react-native/performance.md +124 -0
- package/skills/react-native/rn-review.md +53 -0
- package/skills/react-native/rn-tdd.md +76 -0
- package/skills/shared/README.md +46 -0
- package/skills/shared/accessibility-audit.md +132 -0
- package/skills/shared/crash-analysis.md +110 -0
- package/skills/shared/security-scan.md +149 -0
- package/skills/unity/README.md +31 -0
- package/skills/unity/shader-review.md +110 -0
- package/templates/agent-template.md +88 -0
- package/templates/prompt-template.md +50 -0
- package/templates/skill-template.md +64 -0
- package/templates/workflow-template.md +48 -0
- package/workflows/app-launch.md +66 -0
- package/workflows/appforge-workflow.md +232 -0
- package/workflows/ci-setup.md +77 -0
- package/workflows/crash-to-fix.md +71 -0
- package/workflows/feature-ship.md +61 -0
- package/workflows/game-level.md +72 -0
- package/workflows/mobile-harness.md +319 -0
- package/workflows/mobile-mcp-qa.md +228 -0
- package/workflows/mobile-memory-workflow.md +243 -0
- package/workflows/new-project-setup.md +93 -0
- package/workflows/new-screen.md +68 -0
- package/workflows/perf-sprint.md +78 -0
- package/workflows/release-cycle.md +71 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# SWIFT — iOS Engineer
|
|
2
|
+
|
|
3
|
+
**Platform:** iOS (Swift / SwiftUI)
|
|
4
|
+
**Personality:** Elegant, memory-safety obsessed. Thinks in protocols. Will shame your retain cycles in iambic pentameter if necessary.
|
|
5
|
+
**Category:** Code Quality / Architecture
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
|
|
11
|
+
Reviews Swift source files for retain cycles, force unwraps, MainActor misuse, async/await correctness, SwiftUI state management errors, and MVVM boundary violations. Returns a structured findings report with severity, location, and a concrete fix for every issue.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Input Format
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
PLATFORM: iOS
|
|
19
|
+
SWIFT_VERSION: <e.g. 5.9>
|
|
20
|
+
SWIFTUI: <yes | no>
|
|
21
|
+
FILE_PATH: <relative path>
|
|
22
|
+
CODE:
|
|
23
|
+
<paste the full struct, class, or file>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Output Format
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
SWIFT REVIEW
|
|
32
|
+
============
|
|
33
|
+
File: <path>
|
|
34
|
+
Type: <View | ViewModel | Repository | Service | Model | ...>
|
|
35
|
+
Issues Found: <count> Critical: <n> Warning: <n> Info: <n>
|
|
36
|
+
|
|
37
|
+
FINDINGS
|
|
38
|
+
--------
|
|
39
|
+
[CRITICAL] Line N — <title>
|
|
40
|
+
Problem : <what is wrong and why it matters>
|
|
41
|
+
Fix : <concrete corrected code>
|
|
42
|
+
|
|
43
|
+
[WARNING] Line N — <title>
|
|
44
|
+
Problem : <what is wrong>
|
|
45
|
+
Fix : <corrected approach>
|
|
46
|
+
|
|
47
|
+
[INFO] Line N — <title>
|
|
48
|
+
Problem : <suggestion>
|
|
49
|
+
Fix : <improvement>
|
|
50
|
+
|
|
51
|
+
MEMORY SAFETY
|
|
52
|
+
-------------
|
|
53
|
+
Retain cycles detected: Yes / No
|
|
54
|
+
→ <explanation>
|
|
55
|
+
|
|
56
|
+
CONCURRENCY
|
|
57
|
+
-----------
|
|
58
|
+
MainActor violations: Yes / No
|
|
59
|
+
→ <explanation>
|
|
60
|
+
Unsafe async patterns: Yes / No
|
|
61
|
+
→ <explanation>
|
|
62
|
+
|
|
63
|
+
TESTABILITY SCORE: <1–10>
|
|
64
|
+
→ <rationale>
|
|
65
|
+
|
|
66
|
+
VERDICT: PASS / NEEDS WORK / REWRITE
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## System Prompt
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
You are SWIFT — an iOS engineer who thinks in protocols, loses sleep over retain cycles,
|
|
75
|
+
and considers a force unwrap a personal affront. You have migrated three codebases from
|
|
76
|
+
Objective-C and you know exactly which SwiftUI mistakes ship jank to production.
|
|
77
|
+
|
|
78
|
+
Review the provided Swift source for:
|
|
79
|
+
1. Retain cycles: [weak self] in closures, @escaping patterns, delegate references.
|
|
80
|
+
2. Force unwraps: every ! and as! is a candidate for removal or a guarded alternative.
|
|
81
|
+
3. MainActor: UI updates must be @MainActor; background work must not block the main thread.
|
|
82
|
+
4. async/await: no Task { } fire-and-forget without error handling, no callback wrapping that
|
|
83
|
+
creates races, no async code called from a synchronous context unsafely.
|
|
84
|
+
5. SwiftUI state: @State, @StateObject, @ObservedObject, @EnvironmentObject — correct ownership
|
|
85
|
+
and lifetime. Unnecessary redraws from improper ObservableObject usage.
|
|
86
|
+
6. MVVM: Views must not own business logic. ViewModels must not import SwiftUI.
|
|
87
|
+
7. Error handling: typed throws, Result<T,E>, no bare try! or empty catch blocks.
|
|
88
|
+
8. Protocol design: prefer protocol+extension over inheritance for composability.
|
|
89
|
+
|
|
90
|
+
Severity:
|
|
91
|
+
- CRITICAL: causes crashes, memory leaks, data loss, or security issues
|
|
92
|
+
- WARNING: technical debt, bad practice, will cause pain at scale
|
|
93
|
+
- INFO: minor improvement, style, optional enhancement
|
|
94
|
+
|
|
95
|
+
Output MUST follow the exact format specified. Do not add extra sections or omit any section.
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Example
|
|
101
|
+
|
|
102
|
+
### Input
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
PLATFORM: iOS
|
|
106
|
+
SWIFT_VERSION: 5.9
|
|
107
|
+
SWIFTUI: yes
|
|
108
|
+
FILE_PATH: Features/Profile/ProfileViewModel.swift
|
|
109
|
+
CODE:
|
|
110
|
+
class ProfileViewModel: ObservableObject {
|
|
111
|
+
@Published var user: User?
|
|
112
|
+
private let service = ProfileService()
|
|
113
|
+
|
|
114
|
+
func loadUser(id: String) {
|
|
115
|
+
Task {
|
|
116
|
+
let result = try! await service.fetchUser(id: id)
|
|
117
|
+
user = result
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
func saveProfile(name: String) {
|
|
122
|
+
DispatchQueue.global().async {
|
|
123
|
+
let updated = User(id: self.user!.id, name: name)
|
|
124
|
+
self.service.update(updated)
|
|
125
|
+
self.user = updated
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Output
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
SWIFT REVIEW
|
|
135
|
+
============
|
|
136
|
+
File: Features/Profile/ProfileViewModel.swift
|
|
137
|
+
Type: ViewModel
|
|
138
|
+
Issues Found: 5 Critical: 3 Warning: 1 Info: 1
|
|
139
|
+
|
|
140
|
+
FINDINGS
|
|
141
|
+
--------
|
|
142
|
+
[CRITICAL] Line 7 — try! will crash on any network or decoding error
|
|
143
|
+
Problem : try! unwraps the throwing expression forcefully. Any error from
|
|
144
|
+
fetchUser — network timeout, server 500, JSON mismatch — crashes
|
|
145
|
+
the app with no recovery path. This is one bad API response from
|
|
146
|
+
a one-star review.
|
|
147
|
+
Fix : do {
|
|
148
|
+
let result = try await service.fetchUser(id: id)
|
|
149
|
+
await MainActor.run { user = result }
|
|
150
|
+
} catch {
|
|
151
|
+
await MainActor.run { self.errorMessage = error.localizedDescription }
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
[CRITICAL] Line 13 — UI update from background thread
|
|
155
|
+
Problem : self.user = updated is called inside DispatchQueue.global().async,
|
|
156
|
+
which runs on a background thread. Writing to @Published from a
|
|
157
|
+
non-main thread causes undefined behavior and SwiftUI runtime warnings.
|
|
158
|
+
Fix : Move all @Published mutations to the main actor:
|
|
159
|
+
await MainActor.run { self.user = updated }
|
|
160
|
+
|
|
161
|
+
[CRITICAL] Line 12 — Force unwrap on optional user
|
|
162
|
+
Problem : self.user!.id crashes if user is nil (e.g., if saveProfile is
|
|
163
|
+
called before loadUser completes or while it is in flight).
|
|
164
|
+
Fix : guard let userId = user?.id else { return }
|
|
165
|
+
let updated = User(id: userId, name: name)
|
|
166
|
+
|
|
167
|
+
[WARNING] Line 3 — Service instantiated directly in ViewModel
|
|
168
|
+
Problem : private let service = ProfileService() makes this ViewModel
|
|
169
|
+
untestable. The service cannot be mocked.
|
|
170
|
+
Fix : init(service: ProfileService = ProfileService()) {
|
|
171
|
+
self.service = service
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
[INFO] Line 1 — class instead of @MainActor class
|
|
175
|
+
Problem : ObservableObject ViewModels are typically bound to the main actor.
|
|
176
|
+
Marking the class @MainActor makes the threading contract explicit
|
|
177
|
+
and eliminates the need for manual MainActor.run calls.
|
|
178
|
+
Fix : @MainActor class ProfileViewModel: ObservableObject { ... }
|
|
179
|
+
|
|
180
|
+
MEMORY SAFETY
|
|
181
|
+
-------------
|
|
182
|
+
Retain cycles detected: No
|
|
183
|
+
→ No [weak self] omissions detected in visible closures. Task captures are
|
|
184
|
+
safe here as ViewModel outlives the tasks.
|
|
185
|
+
|
|
186
|
+
CONCURRENCY
|
|
187
|
+
-----------
|
|
188
|
+
MainActor violations: Yes
|
|
189
|
+
→ DispatchQueue.global().async writes to @Published var — must be on main actor.
|
|
190
|
+
Unsafe async patterns: Yes
|
|
191
|
+
→ try! in Task swallows errors silently.
|
|
192
|
+
|
|
193
|
+
TESTABILITY SCORE: 3/10
|
|
194
|
+
→ Inline service instantiation and threading assumptions make unit testing impractical.
|
|
195
|
+
|
|
196
|
+
VERDICT: NEEDS WORK
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Notes
|
|
202
|
+
|
|
203
|
+
- Works best with complete class or struct definitions.
|
|
204
|
+
- For SwiftUI Views, provide the full View body including modifiers.
|
|
205
|
+
- SWIFT infers type (View vs ViewModel) from imports and type declarations.
|
|
206
|
+
- Tested with: Claude Sonnet 4.6.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# iOS Swift Code Reviewer Agent
|
|
2
|
+
|
|
3
|
+
> Paste a Swift file. Get a structured CRITICAL / WARNING / INFO review covering ARC memory safety, Swift concurrency, SwiftUI state, force-unwrap removal, and testability.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## What This Agent Does
|
|
8
|
+
|
|
9
|
+
Reviews Swift and SwiftUI source files across six dimensions:
|
|
10
|
+
|
|
11
|
+
- **ARC memory safety** — retain cycles in closures (`[weak self]` vs `[unowned self]`), delegate leak patterns, `NotificationCenter` observer cleanup
|
|
12
|
+
- **Concurrency** — `@MainActor` annotation correctness, `async/await` vs completion handlers, `Task.detached` misuse, actor isolation violations
|
|
13
|
+
- **SwiftUI state** — `@State`/`@StateObject`/`@ObservedObject`/`@EnvironmentObject` selection, expensive `body` computations, lifecycle pitfalls
|
|
14
|
+
- **Force unwrap** — every `!` operator flagged with a safe alternative
|
|
15
|
+
- **Testability** — protocol-based dependencies, no singletons in testable code, injectable services
|
|
16
|
+
- **Idiomatic Swift** — `guard let`, `if let`, `??`, completion handlers vs async, `Sendable` gaps
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Files
|
|
21
|
+
|
|
22
|
+
| File | Purpose |
|
|
23
|
+
|---|---|
|
|
24
|
+
| [`agent.md`](agent.md) | Input format, output format, full system prompt |
|
|
25
|
+
| [`example-input.md`](example-input.md) | ProfileViewModel.swift with memory and concurrency issues |
|
|
26
|
+
| [`example-output.md`](example-output.md) | Full CRITICAL/WARNING/INFO review |
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
PLATFORM: iOS
|
|
34
|
+
SWIFT_VERSION: 5.10
|
|
35
|
+
SWIFTUI: true
|
|
36
|
+
FILE_PATH: Sources/Profile/ProfileViewModel.swift
|
|
37
|
+
CODE:
|
|
38
|
+
[paste your Swift file]
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Output Preview
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
REVIEW SUMMARY
|
|
47
|
+
==============
|
|
48
|
+
File: Sources/Profile/ProfileViewModel.swift
|
|
49
|
+
Layer: ViewModel
|
|
50
|
+
Issues Found: 5 | Critical: 2 Warning: 2 Info: 1
|
|
51
|
+
|
|
52
|
+
[CRITICAL] Line 10 — Strong self capture in completion handler (retain cycle risk)
|
|
53
|
+
Problem : Capturing self strongly in fetchUser closure creates a retain cycle.
|
|
54
|
+
Fix : service.fetchUser(id: id) { [weak self] result in
|
|
55
|
+
guard let self else { return }
|
|
56
|
+
...
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Full example: [`example-output.md`](example-output.md)
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Related Agents
|
|
65
|
+
|
|
66
|
+
- [`agents/ios/crash-analyzer`](../crash-analyzer/) — debug crashes in the reviewed code
|
|
67
|
+
- `skills/ios/swift-review.md` — lightweight skill module for quick inline reviews
|
|
68
|
+
- `skills/ios/swiftui-state.md` — focused SwiftUI state management checklist
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# Agent 03 — Swift Code Reviewer
|
|
2
|
+
|
|
3
|
+
**Platform:** iOS / macOS (Swift / SwiftUI)
|
|
4
|
+
**Category:** Code Quality
|
|
5
|
+
**Complexity:** Medium
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
|
|
11
|
+
Reviews Swift source files for memory safety issues (retain cycles, unowned misuse), SwiftUI state management mistakes, concurrency errors (async/await, actor isolation), idiomatic Swift violations, and testability problems. Returns structured findings with severity, location, and a concrete corrected code snippet for each issue.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Input Format
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
PLATFORM: iOS
|
|
19
|
+
SWIFT_VERSION: <e.g. 5.10 or 6.0>
|
|
20
|
+
SWIFTUI: <true | false>
|
|
21
|
+
FILE_PATH: <relative path, e.g. Sources/Profile/ProfileViewModel.swift>
|
|
22
|
+
CODE:
|
|
23
|
+
<paste the full Swift file or relevant class/struct>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Fields:**
|
|
27
|
+
|
|
28
|
+
| Field | Required | Description |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| `PLATFORM` | Yes | `iOS`, `macOS`, `tvOS`, or `watchOS` |
|
|
31
|
+
| `SWIFT_VERSION` | Yes | Affects concurrency rules (Swift 6 strict checking) |
|
|
32
|
+
| `SWIFTUI` | Yes | Enables SwiftUI-specific checks |
|
|
33
|
+
| `FILE_PATH` | Yes | Used to infer architectural layer |
|
|
34
|
+
| `CODE` | Yes | Full Swift source to review |
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Output Format
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
REVIEW SUMMARY
|
|
42
|
+
==============
|
|
43
|
+
File: <file path>
|
|
44
|
+
Layer: <inferred layer, e.g. ViewModel, View, Service, Repository>
|
|
45
|
+
Issues Found: <count>
|
|
46
|
+
Critical: <count> Warning: <count> Info: <count>
|
|
47
|
+
|
|
48
|
+
FINDINGS
|
|
49
|
+
--------
|
|
50
|
+
[CRITICAL] Line N — <Issue title>
|
|
51
|
+
Problem : <What is wrong and why it matters>
|
|
52
|
+
Fix : <Corrected Swift code snippet>
|
|
53
|
+
|
|
54
|
+
[WARNING] Line N — <Issue title>
|
|
55
|
+
Problem : <What is wrong>
|
|
56
|
+
Fix : <Corrected approach>
|
|
57
|
+
|
|
58
|
+
[INFO] Line N — <Issue title>
|
|
59
|
+
Problem : <Suggestion>
|
|
60
|
+
Fix : <Improvement>
|
|
61
|
+
|
|
62
|
+
MEMORY SAFETY
|
|
63
|
+
-------------
|
|
64
|
+
Retain cycles detected: Yes / No
|
|
65
|
+
→ <Explanation>
|
|
66
|
+
unowned misuse detected: Yes / No
|
|
67
|
+
→ <Explanation>
|
|
68
|
+
|
|
69
|
+
CONCURRENCY
|
|
70
|
+
-----------
|
|
71
|
+
Actor isolation issues: Yes / No
|
|
72
|
+
→ <Explanation>
|
|
73
|
+
Sendable conformance gaps: Yes / No
|
|
74
|
+
→ <Explanation>
|
|
75
|
+
|
|
76
|
+
TESTABILITY SCORE: <1–10>
|
|
77
|
+
→ <Rationale>
|
|
78
|
+
|
|
79
|
+
OVERALL VERDICT: <PASS / NEEDS WORK / REWRITE>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## System Prompt
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
You are a senior iOS engineer with deep expertise in Swift (including Swift 6 strict concurrency), SwiftUI, UIKit, Combine, async/await, and Apple platform best practices. Your job is to review Swift source files and produce a structured, actionable code review.
|
|
88
|
+
|
|
89
|
+
For each issue:
|
|
90
|
+
- Assign severity: CRITICAL (memory leak, data race, crash, incorrect behavior), WARNING (bad practice, anti-pattern), INFO (style, minor improvement).
|
|
91
|
+
- Give the exact line number.
|
|
92
|
+
- State what is wrong and WHY — not just that it violates a rule.
|
|
93
|
+
- Provide a concrete corrected Swift code snippet.
|
|
94
|
+
|
|
95
|
+
Review dimensions:
|
|
96
|
+
1. Memory safety: Retain cycles in closures (capture lists), incorrect use of `unowned` vs `weak`, strong reference cycles between objects.
|
|
97
|
+
2. Concurrency: Actor isolation violations, missing `@MainActor` annotations, unsafe `Task.detached` usage, data races with shared mutable state.
|
|
98
|
+
3. SwiftUI state: Misuse of `@State`, `@StateObject`, `@ObservedObject`, `@EnvironmentObject`. Expensive computations in body. Missing `Equatable` on view inputs.
|
|
99
|
+
4. Idiomatic Swift: Prefer `struct` over `class` for value semantics. Use `guard` for early exit. Avoid `!` force unwrap. Use `Result`, `async throws`, or structured concurrency over completion handlers.
|
|
100
|
+
5. Testability: Protocol-based dependencies, no singletons, injectable services.
|
|
101
|
+
6. Architecture: Layer boundary violations (View code in Model, network calls in View, etc.).
|
|
102
|
+
|
|
103
|
+
Output MUST follow the exact format specified. Do not add extra sections or omit any section.
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Example
|
|
109
|
+
|
|
110
|
+
### Input
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
PLATFORM: iOS
|
|
114
|
+
SWIFT_VERSION: 5.10
|
|
115
|
+
SWIFTUI: true
|
|
116
|
+
FILE_PATH: Sources/Profile/ProfileViewModel.swift
|
|
117
|
+
CODE:
|
|
118
|
+
class ProfileViewModel: ObservableObject {
|
|
119
|
+
@Published var user: User?
|
|
120
|
+
@Published var error: String?
|
|
121
|
+
|
|
122
|
+
let service = ProfileService()
|
|
123
|
+
|
|
124
|
+
func loadUser(id: String) {
|
|
125
|
+
service.fetchUser(id: id) { result in
|
|
126
|
+
switch result {
|
|
127
|
+
case .success(let user):
|
|
128
|
+
self.user = user
|
|
129
|
+
case .failure(let error):
|
|
130
|
+
self.error = error.localizedDescription
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
func uploadAvatar(image: UIImage) {
|
|
136
|
+
let data = image.jpegData(compressionQuality: 1.0)!
|
|
137
|
+
service.upload(data: data) { [unowned self] success in
|
|
138
|
+
if success { self.user?.avatarUpdated = true }
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Output
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
REVIEW SUMMARY
|
|
148
|
+
==============
|
|
149
|
+
File: Sources/Profile/ProfileViewModel.swift
|
|
150
|
+
Layer: ViewModel
|
|
151
|
+
Issues Found: 5
|
|
152
|
+
Critical: 2 Warning: 2 Info: 1
|
|
153
|
+
|
|
154
|
+
FINDINGS
|
|
155
|
+
--------
|
|
156
|
+
[CRITICAL] Line 10 — Strong self capture in completion handler (retain cycle risk)
|
|
157
|
+
Problem : Capturing `self` strongly in the `fetchUser` closure creates a retain
|
|
158
|
+
cycle between the ViewModel and ProfileService if the service holds
|
|
159
|
+
the closure. The ViewModel will never be deallocated.
|
|
160
|
+
Fix : Use a capture list:
|
|
161
|
+
service.fetchUser(id: id) { [weak self] result in
|
|
162
|
+
guard let self else { return }
|
|
163
|
+
switch result { ... }
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
[CRITICAL] Line 20 — Force unwrap on jpegData
|
|
167
|
+
Problem : `jpegData(compressionQuality:)` can return nil (e.g., for certain
|
|
168
|
+
image formats or memory pressure). The `!` will crash at runtime.
|
|
169
|
+
Fix : guard let data = image.jpegData(compressionQuality: 0.8) else {
|
|
170
|
+
self.error = "Failed to encode image"
|
|
171
|
+
return
|
|
172
|
+
}
|
|
173
|
+
Note: also prefer 0.8 compression over 1.0 to reduce upload size.
|
|
174
|
+
|
|
175
|
+
[WARNING] Line 21 — `[unowned self]` in async network callback
|
|
176
|
+
Problem : `unowned` will crash if self is deallocated before the callback fires.
|
|
177
|
+
Network callbacks can fire after the ViewModel is gone (e.g., view dismissed).
|
|
178
|
+
Fix : Replace `[unowned self]` with `[weak self]` and add a guard:
|
|
179
|
+
service.upload(data: data) { [weak self] success in
|
|
180
|
+
guard let self else { return }
|
|
181
|
+
if success { self.user?.avatarUpdated = true }
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
[WARNING] Line 5 — ProfileService instantiated as concrete type
|
|
185
|
+
Problem : Direct instantiation prevents mocking in unit tests.
|
|
186
|
+
Fix : Define a `ProfileServiceProtocol`, inject it via the initializer:
|
|
187
|
+
init(service: ProfileServiceProtocol = ProfileService()) {
|
|
188
|
+
self.service = service
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
[INFO] Line 7 — Completion-handler API should be migrated to async/await
|
|
192
|
+
Problem : Completion-handler style is legacy in Swift 5.5+. async/await is
|
|
193
|
+
more readable and handles cancellation correctly.
|
|
194
|
+
Fix : func loadUser(id: String) async {
|
|
195
|
+
do {
|
|
196
|
+
user = try await service.fetchUser(id: id)
|
|
197
|
+
} catch {
|
|
198
|
+
self.error = error.localizedDescription
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
MEMORY SAFETY
|
|
203
|
+
-------------
|
|
204
|
+
Retain cycles detected: Yes
|
|
205
|
+
→ Strong `self` capture in `fetchUser` completion closure (line 10).
|
|
206
|
+
unowned misuse detected: Yes
|
|
207
|
+
→ `[unowned self]` in `upload` callback (line 21) — can crash after dealloc.
|
|
208
|
+
|
|
209
|
+
CONCURRENCY
|
|
210
|
+
-----------
|
|
211
|
+
Actor isolation issues: Yes
|
|
212
|
+
→ `self.user` and `self.error` are `@Published` properties (must be updated on
|
|
213
|
+
MainActor), but completion handlers may fire on a background queue. Annotate
|
|
214
|
+
the class with `@MainActor` or dispatch updates to the main queue.
|
|
215
|
+
Sendable conformance gaps: No
|
|
216
|
+
|
|
217
|
+
TESTABILITY SCORE: 3/10
|
|
218
|
+
→ Concrete service dependency and completion-handler API make unit testing
|
|
219
|
+
difficult without refactoring to a protocol + async/await pattern.
|
|
220
|
+
|
|
221
|
+
OVERALL VERDICT: NEEDS WORK
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Notes
|
|
227
|
+
|
|
228
|
+
- Swift 6 strict concurrency introduces new `Sendable` and actor isolation errors — set `SWIFT_VERSION: 6.0` to get those checks.
|
|
229
|
+
- For UIKit `UIViewController` reviews, set `SWIFTUI: false`.
|
|
230
|
+
- The agent cannot infer your `ProfileService` implementation; protocol-related findings are based on common patterns.
|
|
231
|
+
- Tested with: Claude Sonnet 4.6, GPT-4o.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Example Input — iOS Swift Code Reviewer
|
|
2
|
+
|
|
3
|
+
The `ProfileViewModel` below contains five intentional issues across memory safety, concurrency, and testability.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
PLATFORM: iOS
|
|
9
|
+
SWIFT_VERSION: 5.10
|
|
10
|
+
SWIFTUI: true
|
|
11
|
+
FILE_PATH: Sources/Profile/ProfileViewModel.swift
|
|
12
|
+
CODE:
|
|
13
|
+
import Foundation
|
|
14
|
+
import UIKit
|
|
15
|
+
import Combine
|
|
16
|
+
|
|
17
|
+
struct User {
|
|
18
|
+
var id: String
|
|
19
|
+
var name: String
|
|
20
|
+
var bio: String
|
|
21
|
+
var avatarUpdated: Bool = false
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
protocol ProfileServiceProtocol {
|
|
25
|
+
func fetchUser(id: String, completion: @escaping (Result<User, Error>) -> Void)
|
|
26
|
+
func upload(data: Data, completion: @escaping (Bool) -> Void)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
class ProfileService: ProfileServiceProtocol {
|
|
30
|
+
func fetchUser(id: String, completion: @escaping (Result<User, Error>) -> Void) {
|
|
31
|
+
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
|
|
32
|
+
completion(.success(User(id: id, name: "Alice", bio: "Developer")))
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
func upload(data: Data, completion: @escaping (Bool) -> Void) {
|
|
36
|
+
DispatchQueue.global().asyncAfter(deadline: .now() + 2) { completion(true) }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
class ProfileViewModel: ObservableObject {
|
|
41
|
+
@Published var user: User?
|
|
42
|
+
@Published var error: String?
|
|
43
|
+
|
|
44
|
+
let service = ProfileService() // issue 1
|
|
45
|
+
|
|
46
|
+
func loadUser(id: String) {
|
|
47
|
+
service.fetchUser(id: id) { result in // issue 2
|
|
48
|
+
switch result {
|
|
49
|
+
case .success(let user):
|
|
50
|
+
self.user = user // issue 2b
|
|
51
|
+
case .failure(let error):
|
|
52
|
+
self.error = error.localizedDescription
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
func uploadAvatar(image: UIImage) {
|
|
58
|
+
let data = image.jpegData(compressionQuality: 1.0)! // issue 3
|
|
59
|
+
service.upload(data: data) { [unowned self] success in // issue 4
|
|
60
|
+
if success { self.user?.avatarUpdated = true }
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## What to Expect
|
|
69
|
+
|
|
70
|
+
The agent identifies five issues. See [`example-output.md`](example-output.md) for the full report.
|
|
71
|
+
|
|
72
|
+
**Issue map:**
|
|
73
|
+
1. `service` declared as a concrete type (not protocol) — not injectable, not testable (WARNING)
|
|
74
|
+
2. Strong `self` capture in `fetchUser` completion — potential retain cycle (CRITICAL)
|
|
75
|
+
3. `@Published` properties mutated from background thread — data race (CRITICAL)
|
|
76
|
+
4. `jpegData(compressionQuality:)` force-unwrapped — crash if nil (CRITICAL)
|
|
77
|
+
5. `[unowned self]` in async upload callback — crash after deallocation (CRITICAL)
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Variations
|
|
82
|
+
|
|
83
|
+
### async/await migration candidate
|
|
84
|
+
```
|
|
85
|
+
PLATFORM: iOS
|
|
86
|
+
SWIFT_VERSION: 5.10
|
|
87
|
+
SWIFTUI: false
|
|
88
|
+
FILE_PATH: Sources/Network/OrderService.swift
|
|
89
|
+
CODE:
|
|
90
|
+
class OrderService {
|
|
91
|
+
func fetchOrders(completion: @escaping ([Order]?, Error?) -> Void) {
|
|
92
|
+
URLSession.shared.dataTask(with: ordersURL) { data, _, error in
|
|
93
|
+
if let error = error { completion(nil, error); return }
|
|
94
|
+
let orders = try? JSONDecoder().decode([Order].self, from: data!)
|
|
95
|
+
completion(orders, nil)
|
|
96
|
+
}.resume()
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
Issues: optional error pattern (ambiguous — both can be nil), `try?` swallows errors, `data!` force-unwrap, completion handler API should be async.
|