.claude/skills/tooling/macos-crash-recovery/SKILL.md
macOS app crash recovery, state restoration, and autosave patterns. Use when implementing data persistence, app lifecycle, or any feature where data loss on crash is a concern.
npx skillsauth add brdohman/agile-maestro macos-crash-recoveryInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Handle NSApplicationDelegate.applicationShouldTerminate(_:) to prompt for unsaved changes:
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
guard hasUnsavedChanges else { return .terminateNow }
let alert = NSAlert()
alert.messageText = "You have unsaved changes"
alert.informativeText = "Do you want to save before quitting?"
alert.addButton(withTitle: "Save")
alert.addButton(withTitle: "Don't Save")
alert.addButton(withTitle: "Cancel")
switch alert.runModal() {
case .alertFirstButtonReturn:
saveAll()
return .terminateNow
case .alertSecondButtonReturn:
return .terminateNow
default:
return .terminateCancel
}
}
}
Register in your App struct:
@main
struct MyApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup { ContentView() }
}
}
Restore the user's position after relaunch:
struct ContentView: View {
@SceneStorage("selectedTab") private var selectedTab = "accounts"
@SceneStorage("selectedAccountID") private var selectedAccountID: String?
@SceneStorage("sidebarExpanded") private var sidebarExpanded = true
var body: some View {
NavigationSplitView {
Sidebar(selection: $selectedAccountID)
} detail: {
if let accountID = selectedAccountID {
AccountDetail(id: accountID)
}
}
}
}
@SceneStorage survives app restart. Not suitable for sensitive data.
Save periodically and on key events, not just on quit:
actor PersistenceController {
static let shared = PersistenceController()
private let container: NSPersistentContainer
/// Save if there are unsaved changes
func saveIfNeeded() {
let context = container.viewContext
guard context.hasChanges else { return }
do {
try context.save()
} catch {
Logger.persistence.error("Autosave failed: \(error)")
}
}
}
When to autosave:
NSApplication.willResignActiveNotification (user switches away)NSApplication.willTerminateNotification (app quitting)// In your App or ViewModel
NotificationCenter.default.addObserver(
forName: NSApplication.willResignActiveNotification,
object: nil, queue: .main
) { _ in
Task { await PersistenceController.shared.saveIfNeeded() }
}
For operations that modify multiple objects (e.g., importing transactions):
func importTransactions(_ data: [TransactionData]) async throws {
let context = container.newBackgroundContext()
try await context.perform {
for item in data {
let transaction = Transaction(context: context)
transaction.configure(from: item)
}
// Single save at the end — if it fails, nothing is partially committed
try context.save()
}
}
For very large imports, batch saves:
func importLargeDataset(_ items: [ItemData]) async throws {
let context = container.newBackgroundContext()
let batchSize = 100
try await context.perform {
for (index, item) in items.enumerated() {
let entity = Item(context: context)
entity.configure(from: item)
if (index + 1) % batchSize == 0 {
try context.save()
context.reset() // Release memory
}
}
if context.hasChanges { try context.save() }
}
}
Add this to QA testing checklist for any feature that modifies persistent data:
Force-quit the app during the operation (Cmd+Q or kill from Activity Monitor). Relaunch. Verify no data loss and the UI recovers to a valid state.
applicationWillTerminate for saving (not called on force-quit or crash)@SceneStorage for navigation state that users expect to persisttesting
XCTest patterns for macOS Swift apps. Unit tests, async tests, Core Data tests, mock patterns, and assertion reference. Use when writing or reviewing tests.
tools
How to transition workflow state between review stages. Rules for setting review_stage and review_result fields on Stories and Epics.
documentation
Comment structure and rules for task workflow updates. Use when adding any comment to a task during implementation, review, or fix cycles.
testing
Validate task/story/epic/bug/techdebt metadata against schema v2.0. Run after TaskCreate or TaskUpdate to verify compliance. Returns pass/fail with actionable details.