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.
Files changed (194) hide show
  1. package/AGENTS.md +228 -0
  2. package/CLAUDE.md +236 -0
  3. package/LICENSE +21 -0
  4. package/README.md +652 -0
  5. package/agents/android/android-crash-analyzer/README.md +134 -0
  6. package/agents/android/android-crash-analyzer/agent.md +206 -0
  7. package/agents/android/android-crash-analyzer/example-input.md +138 -0
  8. package/agents/android/android-crash-analyzer/example-output.md +225 -0
  9. package/agents/android/anr-investigation/README.md +78 -0
  10. package/agents/android/anr-investigation/agent.md +117 -0
  11. package/agents/android/anr-investigation/example-input.md +62 -0
  12. package/agents/android/anr-investigation/example-output.md +103 -0
  13. package/agents/android/axiom/agent.md +197 -0
  14. package/agents/android/code-reviewer/README.md +108 -0
  15. package/agents/android/code-reviewer/agent.md +210 -0
  16. package/agents/android/code-reviewer/example-input.md +116 -0
  17. package/agents/android/code-reviewer/example-output.md +115 -0
  18. package/agents/android/compose-navigation/README.md +76 -0
  19. package/agents/android/compose-navigation/agent.md +121 -0
  20. package/agents/android/compose-navigation/example-input.md +49 -0
  21. package/agents/android/compose-navigation/example-output.md +325 -0
  22. package/agents/android/compose-screen-builder/README.md +95 -0
  23. package/agents/android/compose-screen-builder/agent.md +170 -0
  24. package/agents/android/compose-screen-builder/example-input.md +40 -0
  25. package/agents/android/compose-screen-builder/example-output.md +568 -0
  26. package/agents/android/compose-ui-reviewer/README.md +73 -0
  27. package/agents/android/compose-ui-reviewer/agent.md +253 -0
  28. package/agents/android/compose-ui-reviewer/example-input.md +84 -0
  29. package/agents/android/compose-ui-reviewer/example-output.md +102 -0
  30. package/agents/android/crash-analyzer/README.md +53 -0
  31. package/agents/android/crash-analyzer/agent.md +295 -0
  32. package/agents/android/memory-leak-analyzer/README.md +79 -0
  33. package/agents/android/memory-leak-analyzer/agent.md +116 -0
  34. package/agents/android/memory-leak-analyzer/example-input.md +78 -0
  35. package/agents/android/memory-leak-analyzer/example-output.md +128 -0
  36. package/agents/cross-platform/accessibility-auditor/README.md +47 -0
  37. package/agents/cross-platform/accessibility-auditor/agent.md +268 -0
  38. package/agents/cross-platform/accessibility-auditor/example-input.md +84 -0
  39. package/agents/cross-platform/accessibility-auditor/example-output.md +132 -0
  40. package/agents/cross-platform/appforge/agent.md +303 -0
  41. package/agents/cross-platform/ci-cd-generator/README.md +65 -0
  42. package/agents/cross-platform/ci-cd-generator/agent.md +422 -0
  43. package/agents/cross-platform/ci-cd-generator/example-input.md +69 -0
  44. package/agents/cross-platform/ci-cd-generator/example-output.md +261 -0
  45. package/agents/cross-platform/crasher/agent.md +221 -0
  46. package/agents/cross-platform/figma/agent.md +247 -0
  47. package/agents/cross-platform/launchpad/agent.md +330 -0
  48. package/agents/cross-platform/mobile-harness/agent.md +422 -0
  49. package/agents/cross-platform/mobile-memory/agent.md +429 -0
  50. package/agents/cross-platform/perf/agent.md +261 -0
  51. package/agents/cross-platform/pipeline/agent.md +288 -0
  52. package/agents/cross-platform/release-notes-generator/README.md +63 -0
  53. package/agents/cross-platform/release-notes-generator/agent.md +218 -0
  54. package/agents/cross-platform/release-notes-generator/example-input.md +71 -0
  55. package/agents/cross-platform/release-notes-generator/example-output.md +96 -0
  56. package/agents/cross-platform/scribe/agent.md +180 -0
  57. package/agents/cross-platform/security-scanner/README.md +61 -0
  58. package/agents/cross-platform/security-scanner/agent.md +287 -0
  59. package/agents/cross-platform/security-scanner/example-input.md +77 -0
  60. package/agents/cross-platform/security-scanner/example-output.md +141 -0
  61. package/agents/cross-platform/sentinel/agent.md +217 -0
  62. package/agents/cross-platform/store-listing-writer/README.md +58 -0
  63. package/agents/cross-platform/store-listing-writer/agent.md +380 -0
  64. package/agents/cross-platform/store-listing-writer/example-input.md +67 -0
  65. package/agents/cross-platform/store-listing-writer/example-output.md +131 -0
  66. package/agents/flutter/bloc-feature-builder/README.md +101 -0
  67. package/agents/flutter/bloc-feature-builder/agent.md +225 -0
  68. package/agents/flutter/bloc-feature-builder/example-input.md +47 -0
  69. package/agents/flutter/bloc-feature-builder/example-output.md +838 -0
  70. package/agents/flutter/dart/agent.md +223 -0
  71. package/agents/flutter/widget-generator/README.md +66 -0
  72. package/agents/flutter/widget-generator/agent.md +264 -0
  73. package/agents/flutter/widget-generator/example-input.md +71 -0
  74. package/agents/flutter/widget-generator/example-output.md +163 -0
  75. package/agents/gaming/forge/agent.md +197 -0
  76. package/agents/gaming/unreal/agent.md +200 -0
  77. package/agents/ios/crash-analyzer/README.md +69 -0
  78. package/agents/ios/crash-analyzer/agent.md +300 -0
  79. package/agents/ios/crash-analyzer/example-input.md +83 -0
  80. package/agents/ios/crash-analyzer/example-output.md +126 -0
  81. package/agents/ios/swift/agent.md +206 -0
  82. package/agents/ios/swift-reviewer/README.md +68 -0
  83. package/agents/ios/swift-reviewer/agent.md +231 -0
  84. package/agents/ios/swift-reviewer/example-input.md +100 -0
  85. package/agents/ios/swift-reviewer/example-output.md +127 -0
  86. package/agents/react-native/bridge/agent.md +212 -0
  87. package/agents/react-native/performance-optimizer/README.md +81 -0
  88. package/agents/react-native/performance-optimizer/agent.md +284 -0
  89. package/agents/react-native/performance-optimizer/example-input.md +104 -0
  90. package/agents/react-native/performance-optimizer/example-output.md +132 -0
  91. package/agents/unity/shader-generator/README.md +77 -0
  92. package/agents/unity/shader-generator/agent.md +270 -0
  93. package/agents/unity/shader-generator/example-input.md +70 -0
  94. package/agents/unity/shader-generator/example-output.md +160 -0
  95. package/agents/unreal/blueprint-advisor/README.md +71 -0
  96. package/agents/unreal/blueprint-advisor/agent.md +311 -0
  97. package/agents/unreal/blueprint-advisor/example-input.md +73 -0
  98. package/agents/unreal/blueprint-advisor/example-output.md +217 -0
  99. package/cli/index.js +1091 -0
  100. package/docs/agent-guide.md +138 -0
  101. package/docs/chatgpt-integration.md +219 -0
  102. package/docs/core-loops.md +143 -0
  103. package/docs/cursor-integration.md +143 -0
  104. package/docs/getting-started.md +132 -0
  105. package/docs/index.html +711 -0
  106. package/docs/installation.md +271 -0
  107. package/docs/mobile-harness.md +160 -0
  108. package/docs/mobile-mcp.md +172 -0
  109. package/docs/mobile-memory.md +349 -0
  110. package/docs/release-process.md +85 -0
  111. package/docs/releases/release-v1.0.0.md +165 -0
  112. package/docs/roadmap/github-issues.md +239 -0
  113. package/docs/skill-categories.md +125 -0
  114. package/docs/vscode-copilot-integration.md +104 -0
  115. package/docs/wiki.md +615 -0
  116. package/install.sh +507 -0
  117. package/mobile-karpathy.md +79 -0
  118. package/package.json +57 -0
  119. package/skills/android/README.md +31 -0
  120. package/skills/android/android-tdd.md +89 -0
  121. package/skills/android/anr-investigation.md +251 -0
  122. package/skills/android/code-review.md +98 -0
  123. package/skills/android/compose-migration.md +48 -0
  124. package/skills/android/compose-review.md +78 -0
  125. package/skills/android/kotlin-modernize.md +51 -0
  126. package/skills/android/memory-leak-investigation.md +256 -0
  127. package/skills/android/proguard-rules.md +48 -0
  128. package/skills/cross-platform/accessibility-audit.md +63 -0
  129. package/skills/cross-platform/api-versioning.md +59 -0
  130. package/skills/cross-platform/clean-code-audit.md +239 -0
  131. package/skills/cross-platform/crash-triage.md +53 -0
  132. package/skills/cross-platform/deeplink-debug.md +57 -0
  133. package/skills/cross-platform/feature-slice.md +48 -0
  134. package/skills/cross-platform/grill-mobile.md +64 -0
  135. package/skills/cross-platform/mobile-mcp-qa.md +193 -0
  136. package/skills/cross-platform/mobile-memory-graph.md +179 -0
  137. package/skills/cross-platform/mobile-memory-save.md +210 -0
  138. package/skills/cross-platform/mobile-memory-search.md +154 -0
  139. package/skills/cross-platform/perf-audit.md +61 -0
  140. package/skills/cross-platform/prd-verification.md +324 -0
  141. package/skills/cross-platform/release-prep.md +64 -0
  142. package/skills/cross-platform/security-audit.md +281 -0
  143. package/skills/cross-platform/store-listing.md +75 -0
  144. package/skills/flutter/README.md +32 -0
  145. package/skills/flutter/dart-modernize.md +47 -0
  146. package/skills/flutter/flutter-review.md +59 -0
  147. package/skills/flutter/flutter-tdd.md +73 -0
  148. package/skills/flutter/widget-extract.md +41 -0
  149. package/skills/flutter/widget-gen.md +131 -0
  150. package/skills/gaming/blueprint-to-cpp.md +51 -0
  151. package/skills/gaming/game-perf.md +54 -0
  152. package/skills/gaming/shader-gen.md +44 -0
  153. package/skills/gaming/unity-tdd.md +85 -0
  154. package/skills/ios/README.md +45 -0
  155. package/skills/ios/data-persistence.md +151 -0
  156. package/skills/ios/ios-tdd.md +90 -0
  157. package/skills/ios/networking.md +139 -0
  158. package/skills/ios/performance.md +122 -0
  159. package/skills/ios/swift-concurrency.md +57 -0
  160. package/skills/ios/swift-review.md +124 -0
  161. package/skills/ios/swiftui-review.md +49 -0
  162. package/skills/ios/swiftui-state.md +122 -0
  163. package/skills/ios/unit-testing.md +144 -0
  164. package/skills/ios/xcode-warnings.md +37 -0
  165. package/skills/react-native/README.md +42 -0
  166. package/skills/react-native/bridge-audit.md +104 -0
  167. package/skills/react-native/expo-optimize.md +55 -0
  168. package/skills/react-native/new-arch-migrate.md +53 -0
  169. package/skills/react-native/performance.md +124 -0
  170. package/skills/react-native/rn-review.md +53 -0
  171. package/skills/react-native/rn-tdd.md +76 -0
  172. package/skills/shared/README.md +46 -0
  173. package/skills/shared/accessibility-audit.md +132 -0
  174. package/skills/shared/crash-analysis.md +110 -0
  175. package/skills/shared/security-scan.md +149 -0
  176. package/skills/unity/README.md +31 -0
  177. package/skills/unity/shader-review.md +110 -0
  178. package/templates/agent-template.md +88 -0
  179. package/templates/prompt-template.md +50 -0
  180. package/templates/skill-template.md +64 -0
  181. package/templates/workflow-template.md +48 -0
  182. package/workflows/app-launch.md +66 -0
  183. package/workflows/appforge-workflow.md +232 -0
  184. package/workflows/ci-setup.md +77 -0
  185. package/workflows/crash-to-fix.md +71 -0
  186. package/workflows/feature-ship.md +61 -0
  187. package/workflows/game-level.md +72 -0
  188. package/workflows/mobile-harness.md +319 -0
  189. package/workflows/mobile-mcp-qa.md +228 -0
  190. package/workflows/mobile-memory-workflow.md +243 -0
  191. package/workflows/new-project-setup.md +93 -0
  192. package/workflows/new-screen.md +68 -0
  193. package/workflows/perf-sprint.md +78 -0
  194. package/workflows/release-cycle.md +71 -0
@@ -0,0 +1,300 @@
1
+ # Agent — iOS Crash Analyzer
2
+
3
+ **Platform:** iOS / macOS (Swift / Objective-C)
4
+ **Category:** Debugging & Crash Analysis
5
+ **Complexity:** Medium
6
+
7
+ ---
8
+
9
+ ## Purpose
10
+
11
+ Analyzes iOS crash reports, symbolicated `.crash` files, Xcode Organizer exports, and Firebase Crashlytics iOS logs to identify the root cause, explain it in developer-friendly language, and provide a safe Swift fix with updated code, edge cases, a testing checklist, and prevention tips.
12
+
13
+ ## Best Use Cases
14
+
15
+ - EXC_BAD_ACCESS / SIGSEGV (null dereference, use-after-free)
16
+ - Force unwrap (!) crashes (unexpectedly found nil)
17
+ - Swift runtime errors (index out of range, cast failures)
18
+ - Retain cycle → memory pressure → crash
19
+ - Main thread checker violations (UI update off main thread)
20
+ - Background task expiration crashes
21
+ - SwiftUI state update after view deallocated
22
+ - Objective-C exceptions (NSException, selector not found)
23
+ - Watchdog terminations (0x8badf00d)
24
+
25
+ ---
26
+
27
+ ## Input Format
28
+
29
+ ```
30
+ PLATFORM: iOS
31
+ APP_VERSION: <e.g. 3.2.1 (build 412)>
32
+ OS_VERSION: <e.g. iOS 17.4>
33
+ DEVICE: <e.g. iPhone 15 Pro>
34
+ USER_ACTION: <What the user was doing before the crash>
35
+ CRASH_LOG:
36
+ <paste the full symbolicated .crash file, Crashlytics log, or Xcode Organizer export>
37
+ RELATED_CODE: <optional: paste the relevant Swift/Objective-C code>
38
+ ```
39
+
40
+ **Fields:**
41
+
42
+ | Field | Required | Description |
43
+ |---|---|---|
44
+ | `PLATFORM` | Yes | Always `iOS` (or `macOS`, `tvOS`, `watchOS`) |
45
+ | `APP_VERSION` | Yes | Helps correlate with release history |
46
+ | `OS_VERSION` | No | Narrows OS-specific bugs |
47
+ | `DEVICE` | No | Narrows device-specific bugs |
48
+ | `USER_ACTION` | Yes | Critical for lifecycle crash diagnosis |
49
+ | `CRASH_LOG` | Yes | Full symbolicated crash report |
50
+ | `RELATED_CODE` | No | Swift/ObjC code at or near the crash site |
51
+
52
+ ---
53
+
54
+ ## Output Format
55
+
56
+ ```
57
+ ## Crash Summary
58
+ <2–3 sentence overview: crash type, where it happened, how severe.>
59
+
60
+ ## Root Cause
61
+ <Precise technical explanation of why the crash occurred — not just the faulting line,
62
+ but the sequence of events and state that led to it.>
63
+
64
+ ## Why This Happens
65
+ <Developer-friendly explanation. Imagine explaining to a junior iOS developer why this
66
+ class of bug exists and why it's easy to introduce.>
67
+
68
+ ## Risk Level
69
+ <CRITICAL | HIGH | MEDIUM | LOW>
70
+ Reproducibility: <Always | Intermittent | Rare | Unknown>
71
+
72
+ ## Recommended Fix
73
+ <Step-by-step fix. Prefer Swift-idiomatic, memory-safe solutions.
74
+ Do not suggest workarounds that mask the bug.>
75
+
76
+ ## Updated Code
77
+ ```swift
78
+ // Before
79
+ <original problematic code>
80
+
81
+ // After
82
+ <corrected code with inline comments>
83
+ ```
84
+
85
+ ## Edge Cases
86
+ - <Other scenarios where the same root cause could trigger>
87
+ - <Related code paths to harden>
88
+
89
+ ## Testing Checklist
90
+ - [ ] <Test step 1>
91
+ - [ ] <Test step 2>
92
+
93
+ ## Prevention Tips
94
+ - <Architectural or coding practice that prevents this class of bug>
95
+ - <Xcode tool, SwiftLint rule, or review checklist item>
96
+ ```
97
+
98
+ ---
99
+
100
+ ## System Prompt
101
+
102
+ ```
103
+ You are a senior iOS crash debugging agent with deep expertise in Swift, Objective-C,
104
+ UIKit, SwiftUI, ARC memory management, Grand Central Dispatch, and Apple platform behavior.
105
+
106
+ Your job is to analyze iOS crash reports and related code like an experienced Apple
107
+ platform engineer would in a production incident investigation.
108
+
109
+ Steps to follow:
110
+ 1. Identify the exception type or signal: EXC_BAD_ACCESS, SIGABRT, force-unwrap nil,
111
+ Swift runtime error, NSException, watchdog termination (0x8badf00d), etc.
112
+ 2. Find the first application frame — skip UIKit, Foundation, Swift runtime, libdispatch frames.
113
+ The first frame with YOUR bundle identifier or module name is the crash origin.
114
+ 3. Check the thread name: "main" thread crashes are often UI-on-background-thread violations;
115
+ background thread crashes may indicate ARC issues or race conditions.
116
+ 4. Trace the call chain from entry point to crash site.
117
+ 5. Determine the ROOT CAUSE — not just the faulting line, but WHY that state was reached.
118
+ 6. Explain it in plain developer-friendly language (Why This Happens).
119
+ 7. Propose the safest, most Swifty fix — prefer guard/let, optional chaining, weak references,
120
+ and DispatchQueue.main.async for UI updates.
121
+ 8. Write corrected Swift code with inline comments.
122
+ 9. List edge cases — other places where the same root cause could appear.
123
+ 10. Write a concrete testing checklist.
124
+ 11. Add prevention tips — SwiftLint rules, Xcode static analysis, architectural patterns.
125
+
126
+ Platform-specific rules:
127
+ - ARC: retain cycles from closures capturing self strongly → use [weak self] or [unowned self]
128
+ (prefer weak unless you can guarantee self outlives the closure)
129
+ - Force unwrap: every ! is a potential crash — suggest guard let, if let, or ??
130
+ - Main thread: UIKit and SwiftUI mutations MUST be on the main thread; use @MainActor or
131
+ DispatchQueue.main.async
132
+ - SwiftUI: @StateObject must be initialized in the view that owns it; @ObservedObject
133
+ can be nil if the parent deallocates
134
+ - Background tasks: watchdog kills the app if a background task runs >30s;
135
+ always call setTaskCompleted(success:) in all code paths
136
+ - Objective-C: unrecognized selector → class doesn't implement the method at runtime;
137
+ NSInvalidArgumentException → nil passed where non-nil required
138
+
139
+ Rules:
140
+ - Do not guess without stating your assumption.
141
+ - If information is missing, state exactly what is missing.
142
+ - The crash report must be symbolicated — if it contains hex addresses with no symbols,
143
+ instruct the developer to symbolicate first using Xcode Organizer or symbolicatecrash.
144
+
145
+ Output MUST use the exact 9-section structure:
146
+ Crash Summary | Root Cause | Why This Happens | Risk Level | Recommended Fix |
147
+ Updated Code | Edge Cases | Testing Checklist | Prevention Tips
148
+ ```
149
+
150
+ ---
151
+
152
+ ## Example
153
+
154
+ ### Input
155
+
156
+ ```
157
+ PLATFORM: iOS
158
+ APP_VERSION: 2.3.0 (build 201)
159
+ OS_VERSION: iOS 17.4
160
+ DEVICE: iPhone 14
161
+ USER_ACTION: User dismissed the profile screen while an image was uploading
162
+ CRASH_LOG:
163
+ Exception Type: EXC_BAD_ACCESS (SIGSEGV)
164
+ Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000010
165
+ Termination Signal: Segmentation fault: 11
166
+
167
+ Thread 0 Crashed:
168
+ 0 libswiftCore.dylib 0x00000001a3b4c000 swift_unknownObjectRetain + 8
169
+ 1 MyApp 0x000000010042a100 ProfileViewModel.uploadAvatar(image:) + 156 (ProfileViewModel.swift:67)
170
+ 2 MyApp 0x000000010041b200 ProfileViewController.uploadButtonTapped(_:) + 84 (ProfileViewController.swift:112)
171
+
172
+ Thread 3:
173
+ 0 libdispatch.dylib 0x00000001a2c10000 _dispatch_call_block_and_release + 12
174
+ 1 MyApp 0x000000010042a300 ProfileViewModel.uploadAvatar.completion + 88 (ProfileViewModel.swift:72)
175
+ RELATED_CODE:
176
+ class ProfileViewModel {
177
+ var onUploadComplete: (() -> Void)?
178
+
179
+ func uploadAvatar(image: UIImage) {
180
+ let data = image.jpegData(compressionQuality: 0.8)!
181
+ uploadService.upload(data: data) { [unowned self] success in // line 72
182
+ if success {
183
+ self.onUploadComplete?()
184
+ }
185
+ }
186
+ }
187
+ }
188
+ ```
189
+
190
+ ### Output
191
+
192
+ ```
193
+ ## Crash Summary
194
+ EXC_BAD_ACCESS (SIGSEGV) in ProfileViewModel.uploadAvatar at line 72 — the upload
195
+ completion closure captures self with [unowned self], but the ViewModel has been
196
+ deallocated by the time the upload completes (user dismissed the screen). Accessing
197
+ a deallocated unowned reference is undefined behavior and always crashes. High severity.
198
+
199
+ ## Root Cause
200
+ [unowned self] creates a non-optional, non-retaining reference to self. Unlike [weak self],
201
+ it does NOT nil-check before access — it assumes self will always be alive when the closure
202
+ fires. When the user dismisses ProfileViewController, the ViewController releases its
203
+ reference to ProfileViewModel. If no other strong reference exists, the ViewModel is
204
+ deallocated. The upload network request continues on a background thread, and when it
205
+ completes, the closure accesses the now-deallocated self via the unowned reference.
206
+ Accessing freed memory causes KERN_INVALID_ADDRESS → SIGSEGV crash.
207
+
208
+ ## Why This Happens
209
+ `unowned` is appropriate only when you can guarantee the captured object will always
210
+ outlive the closure — for example, a closure stored inside the same object it captures.
211
+ Network completion handlers run on background threads after an indeterminate delay.
212
+ The ViewModel has no way to prevent the user from navigating away, so the "self will
213
+ outlive the closure" guarantee is impossible to make here. `[weak self]` is always
214
+ the safe choice for escaping closures in network callbacks.
215
+
216
+ ## Risk Level
217
+ CRITICAL
218
+ Reproducibility: Intermittent — only when the user dismisses the screen during an upload.
219
+ More likely on slow networks. Probability increases with upload file size.
220
+
221
+ ## Recommended Fix
222
+ Step 1: Replace [unowned self] with [weak self] in the upload completion closure.
223
+ Step 2: Add a guard let to safely unwrap self.
224
+ Step 3: Force-unwrap jpegData is also a crash risk — guard it.
225
+ Step 4: Consider cancelling the upload task when the ViewModel is deallocated (deinit).
226
+
227
+ ## Updated Code
228
+ ```swift
229
+ // Before — ProfileViewModel.swift
230
+ func uploadAvatar(image: UIImage) {
231
+ let data = image.jpegData(compressionQuality: 0.8)! // crash if jpegData returns nil
232
+ uploadService.upload(data: data) { [unowned self] success in // CRASH: unowned + deallocated
233
+ if success {
234
+ self.onUploadComplete?()
235
+ }
236
+ }
237
+ }
238
+
239
+ // After — ProfileViewModel.swift
240
+ private var uploadTask: URLSessionTask?
241
+
242
+ func uploadAvatar(image: UIImage) {
243
+ // Guard against nil jpegData (e.g., non-compressible formats)
244
+ guard let data = image.jpegData(compressionQuality: 0.8) else {
245
+ onUploadError?("Failed to encode image")
246
+ return
247
+ }
248
+
249
+ uploadTask = uploadService.upload(data: data) { [weak self] success in
250
+ // Safe: self is nil if ViewModel was deallocated — closure exits cleanly
251
+ guard let self else { return }
252
+ if success {
253
+ self.onUploadComplete?()
254
+ } else {
255
+ self.onUploadError?("Upload failed")
256
+ }
257
+ }
258
+ }
259
+
260
+ deinit {
261
+ // Cancel in-flight upload if the ViewModel is deallocated (user navigated away)
262
+ uploadTask?.cancel()
263
+ }
264
+ ```
265
+
266
+ ## Edge Cases
267
+ - Any other closure in this class that uses [unowned self] in an escaping context
268
+ (network callback, DispatchQueue.async, NotificationCenter observer) has the same risk
269
+ - If uploadService holds a strong reference to the completion block, and the block holds
270
+ a strong reference back to uploadService via self, a retain cycle can keep the ViewModel
271
+ alive longer than expected — mask the crash but cause memory leaks
272
+ - If onUploadComplete captures a UIViewController strongly (e.g., to show an alert),
273
+ and the ViewController is dismissed, calling it after dismiss may cause a different crash
274
+
275
+ ## Testing Checklist
276
+ - [ ] Tap Upload → immediately pop the screen → confirm no crash in Xcode console
277
+ - [ ] Tap Upload on a throttled network (Network Link Conditioner → 3G) → pop → no crash
278
+ - [ ] Run Instruments → Leaks tool → upload several images → confirm no memory leak
279
+ - [ ] Add a breakpoint in deinit → verify it fires when ProfileViewController is dismissed
280
+ - [ ] Test upload with a non-JPEG image format (PNG, HEIC) — guard handles nil jpegData
281
+
282
+ ## Prevention Tips
283
+ - Add a SwiftLint rule or code review checklist: "[unowned self] in escaping closures
284
+ requires a written justification comment explaining why self outlives the closure."
285
+ - Default to [weak self] for ALL network, timer, and NotificationCenter callbacks.
286
+ Only use [unowned self] in non-escaping closures or synchronous callbacks.
287
+ - Enable the Address Sanitizer (ASan) in Xcode scheme diagnostics during development —
288
+ it will catch use-after-free before they reach production.
289
+ - For async/await codebases: use Task { [weak self] in ... } pattern instead of
290
+ completion handlers — Swift's structured concurrency makes these issues easier to reason about.
291
+ ```
292
+
293
+ ---
294
+
295
+ ## Notes
296
+
297
+ - Unsymbolicated crash reports (showing hex addresses like `0x000000010042a100`) cannot be analyzed. Symbolicate first using Xcode Organizer or `symbolicatecrash` in the Terminal.
298
+ - For watchdog terminations (0x8badf00d): paste the full `jetsam_event_log` if available.
299
+ - For SwiftUI crashes: always include the full view hierarchy description if Xcode provides it.
300
+ - Tested with: Claude Sonnet 4.6, GPT-4o.
@@ -0,0 +1,83 @@
1
+ # Example Input — iOS Crash Analyzer
2
+
3
+ ---
4
+
5
+ ```
6
+ PLATFORM: iOS
7
+ APP_VERSION: 2.3.0 (build 201)
8
+ OS_VERSION: iOS 17.4
9
+ DEVICE: iPhone 14
10
+ USER_ACTION: User dismissed the profile screen while an avatar image was still uploading.
11
+
12
+ CRASH_LOG:
13
+ Exception Type: EXC_BAD_ACCESS (SIGSEGV)
14
+ Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000010
15
+ Termination Signal: Segmentation fault: 11
16
+ Termination Reason: Namespace SIGNAL, Code 0xb
17
+
18
+ Thread 0 Crashed:
19
+ 0 libswiftCore.dylib 0x00000001a3b4c000 swift_unknownObjectRetain + 8
20
+ 1 SocialApp 0x000000010042a100 ProfileViewModel.uploadAvatar(image:) + 156 (ProfileViewModel.swift:67)
21
+ 2 SocialApp 0x000000010041b200 ProfileViewController.uploadButtonTapped(_:) + 84 (ProfileViewController.swift:112)
22
+ 3 UIKitCore 0x00000001c4e12000 -[UIControl sendAction:to:forEvent:] + 96
23
+ 4 UIKitCore 0x00000001c4e12100 -[UIControl _sendActionsForEvents:withEvent:] + 284
24
+
25
+ Thread 3 (Network Queue):
26
+ 0 libdispatch.dylib 0x00000001a2c10000 _dispatch_call_block_and_release + 12
27
+ 1 libdispatch.dylib 0x00000001a2c10100 _dispatch_client_callout + 20
28
+ 2 SocialApp 0x000000010042a300 ProfileViewModel.uploadAvatar.completion + 88 (ProfileViewModel.swift:72)
29
+
30
+ RELATED_CODE:
31
+ class ProfileViewModel {
32
+ var onUploadComplete: (() -> Void)?
33
+
34
+ func uploadAvatar(image: UIImage) {
35
+ let data = image.jpegData(compressionQuality: 0.8)! // line 67
36
+ uploadService.upload(data: data) { [unowned self] success in // line 72
37
+ if success {
38
+ self.onUploadComplete?()
39
+ }
40
+ }
41
+ }
42
+ }
43
+ ```
44
+
45
+ ---
46
+
47
+ ## What to Expect
48
+
49
+ The agent produces a full 9-section report. See [`example-output.md`](example-output.md).
50
+
51
+ **Root cause:** `[unowned self]` in an upload callback that fires after the ViewModel was deallocated. `unowned` does not nil-check before access — accessing freed memory causes `EXC_BAD_ACCESS`.
52
+
53
+ ---
54
+
55
+ ## Variations
56
+
57
+ ### Watchdog Termination (0x8badf00d)
58
+ ```
59
+ USER_ACTION: App was in the background for 30 seconds uploading a video.
60
+ CRASH_LOG:
61
+ Exception Type: EXC_CRASH (SIGKILL)
62
+ Exception Codes: 0x0000000000000000, 0x0000000000000000
63
+ Exception Note: EXC_CORPSE_NOTIFY
64
+ Termination Reason: Namespace SPRINGBOARD, Code 0x8badf00d
65
+ Triggered by Thread: 0
66
+ ```
67
+ Root cause: Background task not completing within 30 seconds; `setTaskCompleted()` not called in all code paths.
68
+
69
+ ### Force-unwrap on optional chain
70
+ ```
71
+ USER_ACTION: User navigated to the settings screen immediately after login.
72
+ CRASH_LOG:
73
+ Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
74
+ SocialApp ProfileCoordinator.showSettings() + 124 (ProfileCoordinator.swift:89)
75
+ SocialApp AppCoordinator.handleDeepLink(_:) + 56 (AppCoordinator.swift:142)
76
+ RELATED_CODE:
77
+ func showSettings() {
78
+ let vc = SettingsViewController()
79
+ vc.user = currentUser! // line 89
80
+ navigationController?.pushViewController(vc, animated: true)
81
+ }
82
+ ```
83
+ Root cause: `currentUser` is nil because the user object hasn't been set yet when `showSettings()` is called from a deep link that arrives before the login flow completes.
@@ -0,0 +1,126 @@
1
+ # Example Output — iOS Crash Analyzer
2
+
3
+ Full agent response to the input in [`example-input.md`](example-input.md).
4
+
5
+ ---
6
+
7
+ ## Crash Summary
8
+
9
+ `EXC_BAD_ACCESS (SIGSEGV)` at `ProfileViewModel.swift:72` — the upload completion closure captures `self` with `[unowned self]`. The ViewModel was deallocated when the user dismissed the Profile screen, but the upload callback fired on the network thread afterward. Accessing a deallocated `unowned` reference is undefined behavior that always crashes. **CRITICAL** severity — reproducible whenever the user dismisses the screen while an upload is in flight (proportional to upload latency and user navigation speed).
10
+
11
+ ---
12
+
13
+ ## Root Cause
14
+
15
+ `[unowned self]` creates a non-optional, non-retaining reference to `self`. Unlike `[weak self]`, it does **not** nil-check before access — it simply assumes `self` is always alive when the closure runs.
16
+
17
+ Timeline of the crash:
18
+ 1. User taps Upload → `uploadAvatar()` is called → `uploadService.upload(data:completion:)` starts async network work on Thread 3
19
+ 2. User dismisses `ProfileViewController` → `ProfileViewController` releases its reference to `ProfileViewModel`
20
+ 3. No other strong reference to `ProfileViewModel` exists → ARC deallocates it immediately
21
+ 4. Upload completes on Thread 3 → the completion closure tries to access `self` via the `unowned` reference
22
+ 5. The memory at that address has been freed → `swift_unknownObjectRetain` crashes with `KERN_INVALID_ADDRESS`
23
+
24
+ ---
25
+
26
+ ## Why This Happens
27
+
28
+ Think of `unowned` like a library card that promises the library will always exist. If the library closes (ViewModel deallocated), presenting the card crashes the system — there's nothing there anymore.
29
+
30
+ `weak` is the safe version: it's a membership that automatically becomes nil when the library closes. Checking `guard let self` before proceeding means if the library is gone, you simply walk away rather than crashing.
31
+
32
+ `unowned` is only safe when you can **guarantee** the captured object outlives the closure — for example, closures stored inside the same object, or in parent→child hierarchies where the parent always outlives the child. A network upload callback that can complete after the user navigates away is not that situation.
33
+
34
+ ---
35
+
36
+ ## Risk Level
37
+
38
+ **CRITICAL**
39
+ Reproducibility: **Intermittent** — only when upload is in flight during dismissal
40
+ Frequency increases with: upload file size, network latency, back-navigation speed
41
+ Expected: top 5 Crashlytics issue on any app with image upload + navigation
42
+
43
+ ---
44
+
45
+ ## Recommended Fix
46
+
47
+ **Step 1:** Replace `[unowned self]` with `[weak self]` in the upload completion closure.
48
+ **Step 2:** Add `guard let self else { return }` to safely unwrap before any access.
49
+ **Step 3:** Remove the force-unwrap on `jpegData` — it can return nil.
50
+ **Step 4 (optional):** Cancel the in-flight upload when the ViewModel is deallocated by storing a reference to the upload task.
51
+
52
+ ---
53
+
54
+ ## Updated Code
55
+
56
+ ```swift
57
+ // Before — ProfileViewModel.swift
58
+ class ProfileViewModel {
59
+ var onUploadComplete: (() -> Void)?
60
+
61
+ func uploadAvatar(image: UIImage) {
62
+ let data = image.jpegData(compressionQuality: 0.8)! // crash if nil
63
+ uploadService.upload(data: data) { [unowned self] success in // CRASH: unowned + dealloc
64
+ if success {
65
+ self.onUploadComplete?()
66
+ }
67
+ }
68
+ }
69
+ }
70
+
71
+ // After — ProfileViewModel.swift
72
+ class ProfileViewModel {
73
+ var onUploadComplete: (() -> Void)?
74
+ private var uploadTask: URLSessionTask?
75
+
76
+ func uploadAvatar(image: UIImage) {
77
+ // Guard: jpegData can return nil for certain image types or under memory pressure
78
+ guard let data = image.jpegData(compressionQuality: 0.8) else {
79
+ // notify caller of encoding failure
80
+ return
81
+ }
82
+
83
+ uploadTask = uploadService.upload(data: data) { [weak self] success in
84
+ // Safe: self is nil if ViewModel was deallocated — block exits cleanly
85
+ guard let self else { return }
86
+ if success {
87
+ self.onUploadComplete?()
88
+ }
89
+ }
90
+ }
91
+
92
+ deinit {
93
+ // Cancel the in-flight upload if the ViewModel is released (user navigated away)
94
+ uploadTask?.cancel()
95
+ }
96
+ }
97
+ ```
98
+
99
+ ---
100
+
101
+ ## Edge Cases
102
+
103
+ - **Any other `[unowned self]` in escaping closures** in this class — audit all `uploadService`, `URLSession`, `Timer`, `NotificationCenter`, and `DispatchQueue.async` closures for the same pattern.
104
+ - **`onUploadComplete` itself** — if `onUploadComplete` captures a `UIViewController` strongly (e.g., to show an alert), and the VC is dismissed, calling it after dismiss may crash. Verify the block's capture list.
105
+ - **Background URLSession** — if using `URLSessionConfiguration.background`, completion is delivered to the `URLSessionDelegate`, not a closure. The delegate must hold a `weak` reference back to any object it notifies.
106
+ - **SwiftUI view that drives the upload** — if a `@StateObject` ViewModel hosts this logic, it is owned by the view hierarchy and lives as long as the view. The crash would not occur with `@StateObject` — but it would still occur with `@ObservedObject` if the parent is dismissed.
107
+
108
+ ---
109
+
110
+ ## Testing Checklist
111
+
112
+ - [ ] Tap Upload → immediately tap Back → confirm **no crash** in Xcode console or Crashlytics
113
+ - [ ] Tap Upload on a throttled network (Network Link Conditioner → 3G) → tap Back → no crash
114
+ - [ ] Run the app with Address Sanitizer (Xcode scheme → Diagnostics → Address Sanitizer) → upload → navigate away → confirm no use-after-free report
115
+ - [ ] Verify in Instruments (Leaks) that `ProfileViewModel` is deallocated after screen dismissal — not retained by a leaked closure
116
+ - [ ] Confirm `uploadTask?.cancel()` is called in `deinit` by adding a `print("upload cancelled")` in `deinit` temporarily
117
+
118
+ ---
119
+
120
+ ## Prevention Tips
121
+
122
+ - **Default to `[weak self]`** for all escaping closures in network, timer, and notification callbacks. Use `[unowned self]` only in non-escaping closures or when you can document the lifetime guarantee in a comment.
123
+ - **Add a SwiftLint rule:** `unowned_variable_capture` — flags `unowned` in escaping closure capture lists.
124
+ - **Enable Address Sanitizer** in CI test runs — it catches use-after-free at test time before they reach production.
125
+ - **Migrate to async/await:** `async throws` functions structured with `Task { [weak self] in ... }` make capture semantics explicit and eliminate most completion-handler memory issues.
126
+ - **Code review checklist:** Any PR with a network or timer callback must document the capture list choice (`weak` vs `unowned` vs strong) and the reason.