Install
$ agentstack add skill-cruisediary-apple-app-review-skills-crash-risk-audit ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Skill: Crash Risk Audit
Purpose
Detects force unwraps, force casts, force try, and other crash-prone Swift patterns that can cause unrecoverable crashes during App Store review, violating Guideline 2.1.
Apple Guideline
- Primary: 2.1 — Performance: App Completeness (Crashes)
- Related: 2.1
- Reference:
references/guidelines/2-performance.md
Real-World Rejection Cases
- Case: Force unwrap crash on nil response — reviewer hit edge case with test data — rejected under 2.1
Source: Apple Developer Forums (multiple threads on crash-related rejections) Root cause: Force unwrap (!) causes EXCBADINSTRUCTION crash at review time on edge-case data — reviewers use test accounts and synthetic data that may produce nil where the developer did not expect it
- Case: Force cast
as!crash when server returned unexpected type — reviewer triggered during review
Source: Developer blogs Root cause: Casting without nil check produces unrecoverable crash — reviewers will encounter this on any data type mismatch between the server and the app's model layer
Trigger
Invoke on any iOS/macOS Swift project before App Store submission to identify high-risk crash patterns.
Inputs
| Name | Type | Default | Description | |------|------|---------|-------------| | project_root | path | cwd | iOS/macOS project root | | shared_context | object | nil | Pre-collected context from appstore-full-audit Phase 1 |
Actions
Phase 1: Context Collection
Skip this phase if shared_context is provided.
Glob**/*.swift— collect all Swift source files.Glob**/*.m— collect Objective-C source files.
Phase 2: Checks
- Force unwrap
Run multiple Grep patterns in **/*.swift to catch all force-unwrap variants:
[a-zA-Z0-9_]!\.[a-zA-Z]— force unwrap before property/method access (value!.property)[a-zA-Z0-9_]!— force unwrap before space (let x = value!)[a-zA-Z0-9_]!,— force unwrap in argument list (foo(value!, other))[a-zA-Z0-9_]!\)— force unwrap inside parentheses (foo(value!))[a-zA-Z0-9_]!\]— force unwrap before subscript (arr[value!])[a-zA-Z0-9_]!$— force unwrap at end of line (let x = dict["key"]!)
Each match that is not in a test target or a SwiftUI #Preview block → 🔴 CRITICAL per occurrence. Replace with if let, guard let, or ?? fallback.
- Force cast
Grep pattern as! in **/*.swift. Each match not in a test file → 🔴 CRITICAL. Replace with as? and appropriate nil handling.
- Force try
Grep pattern try! in **/*.swift. Each match not in a test file → 🔴 CRITICAL. Replace with do { try ... } catch { } or try?.
- fatalError in non-exhaustive contexts
Grep pattern fatalError\( in **/*.swift. For each match, Read surrounding context — if used inside a switch for enum exhaustiveness or in required init(coder:) boilerplate stubs, flag → 🟠 HIGH. Any fatalError reachable at runtime on valid user input is a crash risk.
- UI updates on background thread
Grep pattern DispatchQueue\.main\.async|DispatchQueue\.main\.sync in **/*.swift — check for their absence near UI updates. Also Grep for UILabel\.text\s*=|UIImageView\.image\s*=|tableView\.reloadData|collectionView\.reloadData — if these appear outside a DispatchQueue.main block, flag → 🟠 HIGH. Background thread UI mutations cause runtime crashes that are difficult to reproduce but may surface during review.
Phase 3: Output
Collect all findings from Phase 2 and build the prioritised findings list below. Include file paths and line numbers. Omit tiers with no findings.
Output Format
## Crash Risk Audit — Findings
### 🔴 CRITICAL — Guaranteed rejection
- [ ] TODO: Replace force unwrap with guard let or if let — crash risk on nil value — `NetworkManager.swift:45` — Guideline 2.1
- [ ] TODO: Replace `as! UserModel` with `as? UserModel` and handle nil — force cast crash risk — `FeedViewController.swift:112` — Guideline 2.1
- [ ] TODO: Replace try! with do-catch or try? — unhandled error will crash — `DatabaseManager.swift:88` — Guideline 2.1
### 🟠 HIGH — Very likely rejection
- [ ] TODO: Wrap fatalError in required init(coder:) stub with assertionFailure or a logged fallback — `CustomView.swift:22` — Guideline 2.1
- [ ] TODO: Move UILabel.text update inside DispatchQueue.main.async — UI mutation on background thread detected — `DataViewModel.swift:67`
### 🟡 MEDIUM — Possible rejection
- [ ] TODO: Audit remaining fatalError calls to verify they cannot be reached on valid user data paths
### 🟢 LOW — Best practice
- [ ] TODO: Enable Swift strict concurrency checking (SWIFT_STRICT_CONCURRENCY = complete) to catch actor-isolation violations before submission
Tools Used
Glob, Grep, Read
Constraints
- Read-only. No file edits.
- No network calls.
- Skip Phase 1 if
shared_contextis provided by orchestrating agent. - Works on Swift, Objective-C, React Native, Flutter projects.
Quick Commands
Run these in your project root to check manually:
# Count force unwraps (!)
!grep -rn " as! \|try!" . --include="*.swift" | grep -v "//\|fatalError" | wc -l
# List force casts (review each one)
!grep -rn " as! " . --include="*.swift" | grep -v "//"
# Check for force try
!grep -rn "try!" . --include="*.swift" | grep -v "//"
# Check for fatalError usage
!grep -rn "fatalError\(" . --include="*.swift" | grep -v "//"
Swift Anti-Pattern Reference
examples/swift/QualityPatterns.swift
Detection Steps
- Find target files
- Glob:
**/*.swift(exclude*Tests*,*Spec*,*Mock*paths)
- Search for rejection patterns
- Grep
\w![.(]— force unwrap (value!.property,value!() — avoids!=false positives - Grep
\bas!\b— force cast - Grep
\btry!\b— force try - Grep
fatalError\|preconditionFailure— unconditional crash (flag in non-test files) - Grep
\.reloadData()\|\.reloadSections\|beginUpdates— check surrounding context for DispatchQueue.main
- Determine verdict
- Force unwrap in production Swift file → 🟠 HIGH (Guideline 2.1)
- Force cast in production Swift file → 🟠 HIGH
- UITableView/UICollectionView reload outside
DispatchQueue.main→ 🔴 CRITICAL fatalErrorreachable from normal user flow → 🟠 HIGH- No crash-risk patterns in production code → 🟢 pass
- Report
- File path + line number of each occurrence
- Fix: Replace
value!withguard let value = value else { return }orif let; wrap UI updates inDispatchQueue.main.async { }
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: cruisediary
- Source: cruisediary/apple-app-review-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.