[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6E6PDYRY33PNBHH7WSSMGRT":3,"skill-same:01M6E6PDYRY33PNBHH7WSSMGRT":71},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":39,"linkOnly":40,"markdownContent":41,"markdownBody":42,"frontMatter":43,"refName":48,"commitSha":49,"skillDirectory":50,"forksCount":51,"repositoryHtmlUrl":52,"repositoryHomepageUrl":53,"ownerHtmlUrl":54,"ownerBlogUrl":6,"ownerDescription":55,"updatedAt":56,"install":57,"resources":70},"01M6E6PDYRY33PNBHH7WSSMGRT","axiom-storage",null,"Use when asking 'where should I store this data', 'should I use SwiftData or files', 'CloudKit vs iCloud Drive', 'Documents vs Caches', 'local or cloud storage', 'how do I sync data', 'where do app files go' - comprehensive decision framework for all iOS storage options","ComeOnOliver\u002Fskillshub","ComeOnOliver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43215937?v=4","01M6E5JNFECJAAVFE0W7SMWWD2","Haoran Shu","01M6E5JNNDQS7AJZDYFQ8TPZCK",67,"MIT","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002FCharlesWiltgen\u002FAxiom\u002Faxiom-storage\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"axiom storage","storage","decision","store","app","local","options","do","files","drive","use","macos","how","data","ios","cloud","framework","documents","sync","go","2026-11-10 10:12:30",false,"---\nname: axiom-storage\ndescription: Use when asking 'where should I store this data', 'should I use SwiftData or files', 'CloudKit vs iCloud Drive', 'Documents vs Caches', 'local or cloud storage', 'how do I sync data', 'where do app files go' - comprehensive decision framework for all iOS storage options\nlicense: MIT\ncompatibility: iOS 17+, iPadOS 17+, macOS Sonoma+\nmetadata:\n  version: \"1.0.0\"\n  last-updated: \"2025-12-12\"\n---\n\n# iOS Storage Guide\n\n**Purpose**: Navigation hub for ALL storage decisions — database vs files, local vs cloud, specific locations\n**iOS Version**: iOS 17+ (iOS 26+ for latest features)\n**Context**: Complete storage decision framework integrating SwiftData (WWDC 2023), CKSyncEngine (WWDC 2023), and file management best practices\n\n## When to Use This Skill\n\n✅ **Use this skill when**:\n- Starting a new project and choosing storage approach\n- Asking \"where should I store this data?\"\n- Deciding between SwiftData, Core Data, SQLite, or files\n- Choosing between CloudKit and iCloud Drive for sync\n- Determining Documents vs Caches vs Application Support\n- Planning data architecture for offline\u002Fonline scenarios\n- Migrating from one storage solution to another\n- Debugging \"files disappeared\" or \"data not syncing\"\n\n❌ **Do NOT use this skill for**:\n- SwiftData implementation details (use `axiom-swiftdata` skill)\n- SQLite\u002FGRDB specifics (use `axiom-sqlitedata` or `axiom-grdb` skills)\n- CloudKit sync implementation (use `axiom-cloudkit-ref` skill)\n- File protection APIs (use `axiom-file-protection-ref` skill)\n\n**Related Skills**:\n- Existing database skills: `axiom-swiftdata`, `axiom-sqlitedata`, `axiom-grdb`\n- New file skills: `axiom-file-protection-ref`, `axiom-storage-management-ref`, `axiom-storage-diag`\n- New cloud skills: `axiom-cloudkit-ref`, `axiom-icloud-drive-ref`, `axiom-cloud-sync-diag`\n\n## Core Philosophy\n\n> **\"Choose the right tool for your data shape. Then choose the right location.\"**\n\nStorage decisions have two dimensions:\n1. **Format**: How is data structured? (Queryable records vs files)\n2. **Location**: Where is it stored? (Local vs cloud, which directory)\n\nGetting the format wrong forces workarounds. Getting the location wrong causes data loss or backup bloat.\n\n---\n\n## The Complete Decision Tree\n\n### Level 1: Format — What Are You Storing?\n\n```\nWhat is the shape of your data?\n\n├─ STRUCTURED DATA (queryable records, relationships, search)\n│   Examples: User profiles, task lists, notes, contacts, transactions\n│   → Continue to \"Structured Data Path\" below\n│\n└─ FILES (documents, images, videos, downloads, caches)\n    Examples: Photos, PDFs, downloaded content, thumbnails, temp files\n    → Continue to \"File Storage Path\" below\n```\n\n---\n\n## Structured Data Path\n\n### Modern Apps (iOS 17+)\n\n```swift\n\u002F\u002F ✅ CORRECT: SwiftData for modern structured persistence\nimport SwiftData\n\n@Model\nclass Task {\n    var title: String\n    var isCompleted: Bool\n    var dueDate: Date\n\n    init(title: String, isCompleted: Bool = false, dueDate: Date) {\n        self.title = title\n        self.isCompleted = isCompleted\n        self.dueDate = dueDate\n    }\n}\n\n\u002F\u002F Query with type safety\n@Query(sort: \\Task.dueDate) var tasks: [Task]\n```\n\n**Why SwiftData**:\n- Modern Swift-native API (no Objective-C)\n- Type-safe queries\n- Built-in CloudKit sync support\n- Observable models integrate with SwiftUI\n- **Use skill**: `axiom-swiftdata` for implementation details\n\n**When NOT to use SwiftData**:\n- Need advanced SQLite features (FTS5, complex joins)\n- Existing Core Data app (migration overhead)\n- Ultra-performance-critical (direct SQLite is faster)\n\n### Advanced Control Needed\n\n```swift\n\u002F\u002F ✅ CORRECT: SQLiteData or GRDB for advanced features\nimport SQLiteData\n\n\u002F\u002F Full-text search, custom indices, raw SQL when needed\nlet results = try db.prepare(\"SELECT * FROM users WHERE name MATCH ?\", \"John\")\n```\n\n**Use SQLiteData when**:\n- Need full-text search (FTS5)\n- Custom SQL queries and indices\n- Maximum performance (direct SQLite)\n- Migration from existing SQLite database\n- **Use skill**: `axiom-sqlitedata` for modern SQLite patterns\n\n**Use GRDB when**:\n- Need reactive queries (ValueObservation)\n- Complex database operations\n- Type-safe query builders\n- **Use skill**: `axiom-grdb` for advanced patterns\n\n### Legacy Apps (iOS 16 and earlier)\n\n```swift\n\u002F\u002F ❌ LEGACY: Core Data (avoid for new projects)\nimport CoreData\n\n\u002F\u002F NSManagedObject, NSFetchRequest, NSPredicate...\n```\n\n**Only use Core Data if**:\n- Maintaining existing Core Data app\n- Can't upgrade to iOS 17 minimum deployment\n\n---\n\n## File Storage Path\n\n### Decision Tree for Files\n\n```\nWhat kind of file is it?\n\n├─ USER-CREATED CONTENT (documents, photos created by user)\n│   Where: Documents\u002F directory\n│   Backed up: ✅ Yes (iCloud\u002FiTunes)\n│   Purged: ❌ Never\n│   Visible in Files app: ✅ Yes\n│   Example: User's edited photos, documents, exported data\n│   → See \"Documents Directory\" section below\n│\n├─ APP-GENERATED DATA (not user-visible, must persist)\n│   Where: Library\u002FApplication Support\u002F\n│   Backed up: ✅ Yes\n│   Purged: ❌ Never\n│   Visible in Files app: ❌ No\n│   Example: Database files, user settings, downloaded assets\n│   → See \"Application Support Directory\" section below\n│\n├─ RE-DOWNLOADABLE \u002F REGENERABLE CONTENT\n│   Where: Library\u002FCaches\u002F\n│   Backed up: ❌ No (set isExcludedFromBackup)\n│   Purged: ✅ Yes (under storage pressure)\n│   Example: Thumbnails, API responses, downloaded images\n│   → See \"Caches Directory\" section below\n│\n└─ TEMPORARY FILES (can be deleted anytime)\n    Where: tmp\u002F\n    Backed up: ❌ No\n    Purged: ✅ Yes (aggressive, even while app running)\n    Example: Image processing intermediates, export staging\n    → See \"Temporary Directory\" section below\n```\n\n### Documents Directory\n\n```swift\n\u002F\u002F ✅ CORRECT: User-created content in Documents\nfunc saveUserDocument(_ data: Data, filename: String) throws {\n    let documentsURL = FileManager.default.urls(\n        for: .documentDirectory,\n        in: .userDomainMask\n    )[0]\n\n    let fileURL = documentsURL.appendingPathComponent(filename)\n\n    \u002F\u002F Enable file protection\n    try data.write(to: fileURL, options: .completeFileProtection)\n}\n```\n\n**Key rules**:\n- ✅ DO store: User-created documents, exported files, user-visible content\n- ❌ DON'T store: Downloaded data that can be re-fetched, caches, temp files\n- ⚠️ WARNING: Everything here is backed up to iCloud. Large re-downloadable files will bloat backups and may get your app rejected.\n\n**Use skill**: `axiom-file-protection-ref` for encryption options\n\n### Application Support Directory\n\n```swift\n\u002F\u002F ✅ CORRECT: App data in Application Support\nfunc getAppDataURL() -> URL {\n    let appSupportURL = FileManager.default.urls(\n        for: .applicationSupportDirectory,\n        in: .userDomainMask\n    )[0]\n\n    \u002F\u002F Create app-specific subdirectory\n    let appDataURL = appSupportURL.appendingPathComponent(\n        Bundle.main.bundleIdentifier ?? \"AppData\"\n    )\n\n    try? FileManager.default.createDirectory(\n        at: appDataURL,\n        withIntermediateDirectories: true\n    )\n\n    return appDataURL\n}\n```\n\n**Use for**:\n- SwiftData\u002FSQLite database files\n- User preferences\n- Downloaded assets that must persist\n- Configuration files\n\n### Caches Directory\n\n```swift\n\u002F\u002F ✅ CORRECT: Re-downloadable content in Caches\nfunc cacheDownloadedImage(data: Data, for url: URL) throws {\n    let cacheURL = FileManager.default.urls(\n        for: .cachesDirectory,\n        in: .userDomainMask\n    )[0]\n\n    let filename = url.lastPathComponent\n    let fileURL = cacheURL.appendingPathComponent(filename)\n\n    try data.write(to: fileURL)\n\n    \u002F\u002F Mark as excluded from backup (explicit, though Caches is auto-excluded)\n    var resourceValues = URLResourceValues()\n    resourceValues.isExcludedFromBackup = true\n    try fileURL.setResourceValues(resourceValues)\n}\n```\n\n**Key rules**:\n- ✅ The system CAN and WILL delete files here under storage pressure\n- ✅ Always have a way to re-download or regenerate\n- ❌ Don't store anything that can't be recreated\n\n**Use skill**: `axiom-storage-management-ref` for purge policies and disk space management\n\n### Temporary Directory\n\n```swift\n\u002F\u002F ✅ CORRECT: Truly temporary files in tmp\nfunc processImageWithTempFile(image: UIImage) throws {\n    let tmpURL = FileManager.default.temporaryDirectory\n    let tempFileURL = tmpURL.appendingPathComponent(UUID().uuidString + \".jpg\")\n\n    \u002F\u002F Write temp file\n    try image.jpegData(compressionQuality: 0.8)?.write(to: tempFileURL)\n\n    \u002F\u002F Process...\n    processImage(at: tempFileURL)\n\n    \u002F\u002F Clean up (though system will auto-clean eventually)\n    try? FileManager.default.removeItem(at: tempFileURL)\n}\n```\n\n**Key rules**:\n- System can delete files here AT ANY TIME (even while app is running)\n- Always clean up after yourself\n- Don't rely on files persisting between app launches\n\n---\n\n## Cloud Storage Decisions\n\n### Should Data Sync to Cloud?\n\n```\nDoes this data need to sync across user's devices?\n\n├─ NO → Use local storage (paths above)\n│\n└─ YES → What kind of data?\n    │\n    ├─ STRUCTURED DATA (queryable, relationships)\n    │   → Use CloudKit\n    │   → See \"CloudKit Path\" below\n    │\n    ├─ FILES (documents, images)\n    │   → Use iCloud Drive (ubiquitous containers)\n    │   → See \"iCloud Drive Path\" below\n    │\n    └─ SMALL PREFERENCES (\u003C1 MB, key-value pairs)\n        → Use NSUbiquitousKeyValueStore\n        → See \"Key-Value Store\" below\n```\n\n### CloudKit Path (Structured Data Sync)\n\n```swift\n\u002F\u002F ✅ CORRECT: SwiftData with CloudKit sync (iOS 17+)\nimport SwiftData\n\nlet container = try ModelContainer(\n    for: Task.self,\n    configurations: ModelConfiguration(\n        cloudKitDatabase: .private(\"iCloud.com.example.app\")\n    )\n)\n```\n\n**Three approaches to CloudKit**:\n\n1. **SwiftData + CloudKit** (Recommended, iOS 17+):\n   - Automatic sync for SwiftData models\n   - Private database only\n   - Easiest approach\n   - **Use skill**: `axiom-swiftdata` for details\n\n2. **CKSyncEngine** (Custom persistence, iOS 17+):\n   - For SQLite, GRDB, or custom stores\n   - Manages sync automatically\n   - Modern replacement for manual CloudKit\n   - **Use skill**: `axiom-cloudkit-ref` for CKSyncEngine patterns\n\n3. **Raw CloudKit APIs** (Legacy):\n   - CKContainer, CKDatabase, CKRecord\n   - Manual sync management\n   - Only if CKSyncEngine doesn't fit\n   - **Use skill**: `axiom-cloudkit-ref` for raw API reference\n\n### iCloud Drive Path (File Sync)\n\n```swift\n\u002F\u002F ✅ CORRECT: iCloud Drive for file-based sync\nfunc saveToICloud(_ data: Data, filename: String) throws {\n    \u002F\u002F Get ubiquitous container\n    guard let iCloudURL = FileManager.default.url(\n        forUbiquityContainerIdentifier: nil\n    ) else {\n        throw StorageError.iCloudUnavailable\n    }\n\n    let documentsURL = iCloudURL.appendingPathComponent(\"Documents\")\n    try FileManager.default.createDirectory(\n        at: documentsURL,\n        withIntermediateDirectories: true\n    )\n\n    let fileURL = documentsURL.appendingPathComponent(filename)\n    try data.write(to: fileURL)\n}\n```\n\n**When to use iCloud Drive**:\n- User-created documents that sync\n- File-based collaboration\n- Simple file sync (like Dropbox)\n\n**Use skill**: `axiom-icloud-drive-ref` for implementation details\n\n### Key-Value Store (Small Preferences)\n\n```swift\n\u002F\u002F ✅ CORRECT: Small synced preferences\nlet store = NSUbiquitousKeyValueStore.default\n\nstore.set(true, forKey: \"darkModeEnabled\")\nstore.set(2.0, forKey: \"textSize\")\nstore.synchronize()\n```\n\n**Limitations**:\n- Max 1 MB total storage\n- Max 1024 keys\n- Max 1 MB per value\n- For preferences ONLY, not data storage\n\n---\n\n## Common Patterns and Anti-Patterns\n\n### ✅ DO: Choose Based on Data Shape\n\n```swift\n\u002F\u002F ✅ CORRECT: Structured data → SwiftData\n@Model\nclass Note {\n    var title: String\n    var content: String\n    var tags: [Tag]  \u002F\u002F Relationships\n}\n\n\u002F\u002F ✅ CORRECT: Files → FileManager + proper directory\nlet imageData = capturedPhoto.jpegData(compressionQuality: 0.9)\ntry imageData?.write(to: documentsURL.appendingPathComponent(\"photo.jpg\"))\n```\n\n### ❌ DON'T: Use Files for Structured Data\n\n```swift\n\u002F\u002F ❌ WRONG: Storing queryable data as JSON files\nlet tasks = [Task(...), Task(...), Task(...)]\nlet jsonData = try JSONEncoder().encode(tasks)\ntry jsonData.write(to: appSupportURL.appendingPathComponent(\"tasks.json\"))\n\n\u002F\u002F Why it's wrong:\n\u002F\u002F - Can't query individual tasks\n\u002F\u002F - Can't filter or sort efficiently\n\u002F\u002F - No relationships\n\u002F\u002F - Entire file loaded into memory\n\u002F\u002F - Concurrent access issues\n\n\u002F\u002F ✅ CORRECT: Use SwiftData instead\n@Model class Task { ... }\n```\n\n### ❌ DON'T: Store Re-downloadable Content in Documents\n\n```swift\n\u002F\u002F ❌ WRONG: Downloaded images in Documents (bloats backup!)\nfunc downloadProfileImage(url: URL) throws {\n    let data = try Data(contentsOf: url)\n    let documentsURL = FileManager.default.urls(\n        for: .documentDirectory,\n        in: .userDomainMask\n    )[0]\n    try data.write(to: documentsURL.appendingPathComponent(\"profile.jpg\"))\n}\n\n\u002F\u002F ✅ CORRECT: Use Caches instead\nfunc downloadProfileImage(url: URL) throws {\n    let data = try Data(contentsOf: url)\n    let cacheURL = FileManager.default.urls(\n        for: .cachesDirectory,\n        in: .userDomainMask\n    )[0]\n    let fileURL = cacheURL.appendingPathComponent(\"profile.jpg\")\n    try data.write(to: fileURL)\n\n    \u002F\u002F Mark excluded from backup\n    var resourceValues = URLResourceValues()\n    resourceValues.isExcludedFromBackup = true\n    try fileURL.setResourceValues(resourceValues)\n}\n```\n\n### ❌ DON'T: Use CloudKit for Simple File Sync\n\n```swift\n\u002F\u002F ❌ WRONG: Storing files as CKAssets with manual sync\nlet asset = CKAsset(fileURL: documentURL)\nlet record = CKRecord(recordType: \"Document\")\nrecord[\"file\"] = asset\n\u002F\u002F ... manual upload, conflict handling, etc.\n\n\u002F\u002F ✅ CORRECT: Use iCloud Drive for files\n\u002F\u002F Files automatically sync via ubiquitous container\ntry data.write(to: iCloudDocumentsURL.appendingPathComponent(\"doc.pdf\"))\n```\n\n---\n\n## Quick Reference Table\n\n| Data Type | Format | Local Location | Cloud Sync | Use Skill |\n|-----------|--------|----------------|------------|-----------|\n| User tasks, notes | Structured | Application Support | SwiftData + CloudKit | `axiom-swiftdata` → `axiom-cloudkit-ref` |\n| User photos (created) | File | Documents | iCloud Drive | `axiom-file-protection-ref` → `axiom-icloud-drive-ref` |\n| Downloaded images | File | Caches | None (re-download) | `axiom-storage-management-ref` |\n| Thumbnails | File | Caches | None (regenerate) | `axiom-storage-management-ref` |\n| Database file | File | Application Support | CKSyncEngine (if custom) | `axiom-sqlitedata` → `axiom-cloudkit-ref` |\n| Temp processing | File | tmp | None | N\u002FA |\n| User settings | Key-Value | UserDefaults | NSUbiquitousKeyValueStore | N\u002FA |\n\n---\n\n## tvOS Storage\n\n**tvOS has no persistent local storage.** This catches every iOS developer.\n\n| Directory | tvOS Behavior |\n|-----------|--------------|\n| Documents | Does not exist |\n| Application Support | System can delete when app is not running |\n| Caches | System deletes at any time |\n| tmp | System deletes at any time |\n| UserDefaults | 500 KB limit (vs ~4 MB on iOS) |\n\n**Every local file can vanish between app launches.** Your tvOS app must survive starting from zero.\n\n**Recommended**: Use iCloud (CloudKit, NSUbiquitousKeyValueStore, or iCloud Drive) as primary storage. Treat local files as cache only. See `axiom-tvos` for full tvOS storage patterns.\n\n---\n\n## Debugging: Data Missing or Not Syncing?\n\n**Files disappeared**:\n- Check if stored in Caches or tmp (system purged them)\n- Check file protection level (may be inaccessible when locked)\n- **Use skill**: `axiom-storage-diag`\n\n**Backup too large**:\n- Check if re-downloadable content is in Documents (should be in Caches)\n- Check if `isExcludedFromBackup` is set on large files\n- **Use skill**: `axiom-storage-management-ref`\n\n**Data not syncing**:\n- CloudKit: Check CKSyncEngine status, account availability\n  - **Use skill**: `axiom-cloud-sync-diag`\n- iCloud Drive: Check ubiquitous container entitlements, file coordinator\n  - **Use skill**: `axiom-icloud-drive-ref`, `axiom-cloud-sync-diag`\n\n---\n\n## Migration Checklist\n\nWhen changing storage approach:\n\n**Database to Database** (e.g., Core Data → SwiftData):\n- [ ] Create SwiftData models matching Core Data entities\n- [ ] Write migration code to copy data\n- [ ] Test with production-size datasets\n- [ ] Keep old database for rollback\n\n**Files to Database**:\n- [ ] Identify all JSON\u002Fplist files storing structured data\n- [ ] Create SwiftData models\n- [ ] Write one-time migration on first launch\n- [ ] Verify all data migrated, then delete old files\n\n**Local to Cloud**:\n- [ ] Ensure proper entitlements (CloudKit\u002FiCloud)\n- [ ] Handle initial upload carefully (bandwidth)\n- [ ] Test conflict resolution\n- [ ] Provide user control (opt-in)\n\n---\n\n**Last Updated**: 2025-12-12\n**Skill Type**: Discipline\n**Related WWDC Sessions**:\n- WWDC 2023-10187: Meet SwiftData\n- WWDC 2023-10188: Sync to iCloud with CKSyncEngine\n- WWDC 2024-10137: What's new in SwiftData\n\n","# iOS Storage Guide\n\n**Purpose**: Navigation hub for ALL storage decisions — database vs files, local vs cloud, specific locations\n**iOS Version**: iOS 17+ (iOS 26+ for latest features)\n**Context**: Complete storage decision framework integrating SwiftData (WWDC 2023), CKSyncEngine (WWDC 2023), and file management best practices\n\n## When to Use This Skill\n\n✅ **Use this skill when**:\n- Starting a new project and choosing storage approach\n- Asking \"where should I store this data?\"\n- Deciding between SwiftData, Core Data, SQLite, or files\n- Choosing between CloudKit and iCloud Drive for sync\n- Determining Documents vs Caches vs Application Support\n- Planning data architecture for offline\u002Fonline scenarios\n- Migrating from one storage solution to another\n- Debugging \"files disappeared\" or \"data not syncing\"\n\n❌ **Do NOT use this skill for**:\n- SwiftData implementation details (use `axiom-swiftdata` skill)\n- SQLite\u002FGRDB specifics (use `axiom-sqlitedata` or `axiom-grdb` skills)\n- CloudKit sync implementation (use `axiom-cloudkit-ref` skill)\n- File protection APIs (use `axiom-file-protection-ref` skill)\n\n**Related Skills**:\n- Existing database skills: `axiom-swiftdata`, `axiom-sqlitedata`, `axiom-grdb`\n- New file skills: `axiom-file-protection-ref`, `axiom-storage-management-ref`, `axiom-storage-diag`\n- New cloud skills: `axiom-cloudkit-ref`, `axiom-icloud-drive-ref`, `axiom-cloud-sync-diag`\n\n## Core Philosophy\n\n> **\"Choose the right tool for your data shape. Then choose the right location.\"**\n\nStorage decisions have two dimensions:\n1. **Format**: How is data structured? (Queryable records vs files)\n2. **Location**: Where is it stored? (Local vs cloud, which directory)\n\nGetting the format wrong forces workarounds. Getting the location wrong causes data loss or backup bloat.\n\n---\n\n## The Complete Decision Tree\n\n### Level 1: Format — What Are You Storing?\n\n```\nWhat is the shape of your data?\n\n├─ STRUCTURED DATA (queryable records, relationships, search)\n│   Examples: User profiles, task lists, notes, contacts, transactions\n│   → Continue to \"Structured Data Path\" below\n│\n└─ FILES (documents, images, videos, downloads, caches)\n    Examples: Photos, PDFs, downloaded content, thumbnails, temp files\n    → Continue to \"File Storage Path\" below\n```\n\n---\n\n## Structured Data Path\n\n### Modern Apps (iOS 17+)\n\n```swift\n\u002F\u002F ✅ CORRECT: SwiftData for modern structured persistence\nimport SwiftData\n\n@Model\nclass Task {\n    var title: String\n    var isCompleted: Bool\n    var dueDate: Date\n\n    init(title: String, isCompleted: Bool = false, dueDate: Date) {\n        self.title = title\n        self.isCompleted = isCompleted\n        self.dueDate = dueDate\n    }\n}\n\n\u002F\u002F Query with type safety\n@Query(sort: \\Task.dueDate) var tasks: [Task]\n```\n\n**Why SwiftData**:\n- Modern Swift-native API (no Objective-C)\n- Type-safe queries\n- Built-in CloudKit sync support\n- Observable models integrate with SwiftUI\n- **Use skill**: `axiom-swiftdata` for implementation details\n\n**When NOT to use SwiftData**:\n- Need advanced SQLite features (FTS5, complex joins)\n- Existing Core Data app (migration overhead)\n- Ultra-performance-critical (direct SQLite is faster)\n\n### Advanced Control Needed\n\n```swift\n\u002F\u002F ✅ CORRECT: SQLiteData or GRDB for advanced features\nimport SQLiteData\n\n\u002F\u002F Full-text search, custom indices, raw SQL when needed\nlet results = try db.prepare(\"SELECT * FROM users WHERE name MATCH ?\", \"John\")\n```\n\n**Use SQLiteData when**:\n- Need full-text search (FTS5)\n- Custom SQL queries and indices\n- Maximum performance (direct SQLite)\n- Migration from existing SQLite database\n- **Use skill**: `axiom-sqlitedata` for modern SQLite patterns\n\n**Use GRDB when**:\n- Need reactive queries (ValueObservation)\n- Complex database operations\n- Type-safe query builders\n- **Use skill**: `axiom-grdb` for advanced patterns\n\n### Legacy Apps (iOS 16 and earlier)\n\n```swift\n\u002F\u002F ❌ LEGACY: Core Data (avoid for new projects)\nimport CoreData\n\n\u002F\u002F NSManagedObject, NSFetchRequest, NSPredicate...\n```\n\n**Only use Core Data if**:\n- Maintaining existing Core Data app\n- Can't upgrade to iOS 17 minimum deployment\n\n---\n\n## File Storage Path\n\n### Decision Tree for Files\n\n```\nWhat kind of file is it?\n\n├─ USER-CREATED CONTENT (documents, photos created by user)\n│   Where: Documents\u002F directory\n│   Backed up: ✅ Yes (iCloud\u002FiTunes)\n│   Purged: ❌ Never\n│   Visible in Files app: ✅ Yes\n│   Example: User's edited photos, documents, exported data\n│   → See \"Documents Directory\" section below\n│\n├─ APP-GENERATED DATA (not user-visible, must persist)\n│   Where: Library\u002FApplication Support\u002F\n│   Backed up: ✅ Yes\n│   Purged: ❌ Never\n│   Visible in Files app: ❌ No\n│   Example: Database files, user settings, downloaded assets\n│   → See \"Application Support Directory\" section below\n│\n├─ RE-DOWNLOADABLE \u002F REGENERABLE CONTENT\n│   Where: Library\u002FCaches\u002F\n│   Backed up: ❌ No (set isExcludedFromBackup)\n│   Purged: ✅ Yes (under storage pressure)\n│   Example: Thumbnails, API responses, downloaded images\n│   → See \"Caches Directory\" section below\n│\n└─ TEMPORARY FILES (can be deleted anytime)\n    Where: tmp\u002F\n    Backed up: ❌ No\n    Purged: ✅ Yes (aggressive, even while app running)\n    Example: Image processing intermediates, export staging\n    → See \"Temporary Directory\" section below\n```\n\n### Documents Directory\n\n```swift\n\u002F\u002F ✅ CORRECT: User-created content in Documents\nfunc saveUserDocument(_ data: Data, filename: String) throws {\n    let documentsURL = FileManager.default.urls(\n        for: .documentDirectory,\n        in: .userDomainMask\n    )[0]\n\n    let fileURL = documentsURL.appendingPathComponent(filename)\n\n    \u002F\u002F Enable file protection\n    try data.write(to: fileURL, options: .completeFileProtection)\n}\n```\n\n**Key rules**:\n- ✅ DO store: User-created documents, exported files, user-visible content\n- ❌ DON'T store: Downloaded data that can be re-fetched, caches, temp files\n- ⚠️ WARNING: Everything here is backed up to iCloud. Large re-downloadable files will bloat backups and may get your app rejected.\n\n**Use skill**: `axiom-file-protection-ref` for encryption options\n\n### Application Support Directory\n\n```swift\n\u002F\u002F ✅ CORRECT: App data in Application Support\nfunc getAppDataURL() -> URL {\n    let appSupportURL = FileManager.default.urls(\n        for: .applicationSupportDirectory,\n        in: .userDomainMask\n    )[0]\n\n    \u002F\u002F Create app-specific subdirectory\n    let appDataURL = appSupportURL.appendingPathComponent(\n        Bundle.main.bundleIdentifier ?? \"AppData\"\n    )\n\n    try? FileManager.default.createDirectory(\n        at: appDataURL,\n        withIntermediateDirectories: true\n    )\n\n    return appDataURL\n}\n```\n\n**Use for**:\n- SwiftData\u002FSQLite database files\n- User preferences\n- Downloaded assets that must persist\n- Configuration files\n\n### Caches Directory\n\n```swift\n\u002F\u002F ✅ CORRECT: Re-downloadable content in Caches\nfunc cacheDownloadedImage(data: Data, for url: URL) throws {\n    let cacheURL = FileManager.default.urls(\n        for: .cachesDirectory,\n        in: .userDomainMask\n    )[0]\n\n    let filename = url.lastPathComponent\n    let fileURL = cacheURL.appendingPathComponent(filename)\n\n    try data.write(to: fileURL)\n\n    \u002F\u002F Mark as excluded from backup (explicit, though Caches is auto-excluded)\n    var resourceValues = URLResourceValues()\n    resourceValues.isExcludedFromBackup = true\n    try fileURL.setResourceValues(resourceValues)\n}\n```\n\n**Key rules**:\n- ✅ The system CAN and WILL delete files here under storage pressure\n- ✅ Always have a way to re-download or regenerate\n- ❌ Don't store anything that can't be recreated\n\n**Use skill**: `axiom-storage-management-ref` for purge policies and disk space management\n\n### Temporary Directory\n\n```swift\n\u002F\u002F ✅ CORRECT: Truly temporary files in tmp\nfunc processImageWithTempFile(image: UIImage) throws {\n    let tmpURL = FileManager.default.temporaryDirectory\n    let tempFileURL = tmpURL.appendingPathComponent(UUID().uuidString + \".jpg\")\n\n    \u002F\u002F Write temp file\n    try image.jpegData(compressionQuality: 0.8)?.write(to: tempFileURL)\n\n    \u002F\u002F Process...\n    processImage(at: tempFileURL)\n\n    \u002F\u002F Clean up (though system will auto-clean eventually)\n    try? FileManager.default.removeItem(at: tempFileURL)\n}\n```\n\n**Key rules**:\n- System can delete files here AT ANY TIME (even while app is running)\n- Always clean up after yourself\n- Don't rely on files persisting between app launches\n\n---\n\n## Cloud Storage Decisions\n\n### Should Data Sync to Cloud?\n\n```\nDoes this data need to sync across user's devices?\n\n├─ NO → Use local storage (paths above)\n│\n└─ YES → What kind of data?\n    │\n    ├─ STRUCTURED DATA (queryable, relationships)\n    │   → Use CloudKit\n    │   → See \"CloudKit Path\" below\n    │\n    ├─ FILES (documents, images)\n    │   → Use iCloud Drive (ubiquitous containers)\n    │   → See \"iCloud Drive Path\" below\n    │\n    └─ SMALL PREFERENCES (\u003C1 MB, key-value pairs)\n        → Use NSUbiquitousKeyValueStore\n        → See \"Key-Value Store\" below\n```\n\n### CloudKit Path (Structured Data Sync)\n\n```swift\n\u002F\u002F ✅ CORRECT: SwiftData with CloudKit sync (iOS 17+)\nimport SwiftData\n\nlet container = try ModelContainer(\n    for: Task.self,\n    configurations: ModelConfiguration(\n        cloudKitDatabase: .private(\"iCloud.com.example.app\")\n    )\n)\n```\n\n**Three approaches to CloudKit**:\n\n1. **SwiftData + CloudKit** (Recommended, iOS 17+):\n   - Automatic sync for SwiftData models\n   - Private database only\n   - Easiest approach\n   - **Use skill**: `axiom-swiftdata` for details\n\n2. **CKSyncEngine** (Custom persistence, iOS 17+):\n   - For SQLite, GRDB, or custom stores\n   - Manages sync automatically\n   - Modern replacement for manual CloudKit\n   - **Use skill**: `axiom-cloudkit-ref` for CKSyncEngine patterns\n\n3. **Raw CloudKit APIs** (Legacy):\n   - CKContainer, CKDatabase, CKRecord\n   - Manual sync management\n   - Only if CKSyncEngine doesn't fit\n   - **Use skill**: `axiom-cloudkit-ref` for raw API reference\n\n### iCloud Drive Path (File Sync)\n\n```swift\n\u002F\u002F ✅ CORRECT: iCloud Drive for file-based sync\nfunc saveToICloud(_ data: Data, filename: String) throws {\n    \u002F\u002F Get ubiquitous container\n    guard let iCloudURL = FileManager.default.url(\n        forUbiquityContainerIdentifier: nil\n    ) else {\n        throw StorageError.iCloudUnavailable\n    }\n\n    let documentsURL = iCloudURL.appendingPathComponent(\"Documents\")\n    try FileManager.default.createDirectory(\n        at: documentsURL,\n        withIntermediateDirectories: true\n    )\n\n    let fileURL = documentsURL.appendingPathComponent(filename)\n    try data.write(to: fileURL)\n}\n```\n\n**When to use iCloud Drive**:\n- User-created documents that sync\n- File-based collaboration\n- Simple file sync (like Dropbox)\n\n**Use skill**: `axiom-icloud-drive-ref` for implementation details\n\n### Key-Value Store (Small Preferences)\n\n```swift\n\u002F\u002F ✅ CORRECT: Small synced preferences\nlet store = NSUbiquitousKeyValueStore.default\n\nstore.set(true, forKey: \"darkModeEnabled\")\nstore.set(2.0, forKey: \"textSize\")\nstore.synchronize()\n```\n\n**Limitations**:\n- Max 1 MB total storage\n- Max 1024 keys\n- Max 1 MB per value\n- For preferences ONLY, not data storage\n\n---\n\n## Common Patterns and Anti-Patterns\n\n### ✅ DO: Choose Based on Data Shape\n\n```swift\n\u002F\u002F ✅ CORRECT: Structured data → SwiftData\n@Model\nclass Note {\n    var title: String\n    var content: String\n    var tags: [Tag]  \u002F\u002F Relationships\n}\n\n\u002F\u002F ✅ CORRECT: Files → FileManager + proper directory\nlet imageData = capturedPhoto.jpegData(compressionQuality: 0.9)\ntry imageData?.write(to: documentsURL.appendingPathComponent(\"photo.jpg\"))\n```\n\n### ❌ DON'T: Use Files for Structured Data\n\n```swift\n\u002F\u002F ❌ WRONG: Storing queryable data as JSON files\nlet tasks = [Task(...), Task(...), Task(...)]\nlet jsonData = try JSONEncoder().encode(tasks)\ntry jsonData.write(to: appSupportURL.appendingPathComponent(\"tasks.json\"))\n\n\u002F\u002F Why it's wrong:\n\u002F\u002F - Can't query individual tasks\n\u002F\u002F - Can't filter or sort efficiently\n\u002F\u002F - No relationships\n\u002F\u002F - Entire file loaded into memory\n\u002F\u002F - Concurrent access issues\n\n\u002F\u002F ✅ CORRECT: Use SwiftData instead\n@Model class Task { ... }\n```\n\n### ❌ DON'T: Store Re-downloadable Content in Documents\n\n```swift\n\u002F\u002F ❌ WRONG: Downloaded images in Documents (bloats backup!)\nfunc downloadProfileImage(url: URL) throws {\n    let data = try Data(contentsOf: url)\n    let documentsURL = FileManager.default.urls(\n        for: .documentDirectory,\n        in: .userDomainMask\n    )[0]\n    try data.write(to: documentsURL.appendingPathComponent(\"profile.jpg\"))\n}\n\n\u002F\u002F ✅ CORRECT: Use Caches instead\nfunc downloadProfileImage(url: URL) throws {\n    let data = try Data(contentsOf: url)\n    let cacheURL = FileManager.default.urls(\n        for: .cachesDirectory,\n        in: .userDomainMask\n    )[0]\n    let fileURL = cacheURL.appendingPathComponent(\"profile.jpg\")\n    try data.write(to: fileURL)\n\n    \u002F\u002F Mark excluded from backup\n    var resourceValues = URLResourceValues()\n    resourceValues.isExcludedFromBackup = true\n    try fileURL.setResourceValues(resourceValues)\n}\n```\n\n### ❌ DON'T: Use CloudKit for Simple File Sync\n\n```swift\n\u002F\u002F ❌ WRONG: Storing files as CKAssets with manual sync\nlet asset = CKAsset(fileURL: documentURL)\nlet record = CKRecord(recordType: \"Document\")\nrecord[\"file\"] = asset\n\u002F\u002F ... manual upload, conflict handling, etc.\n\n\u002F\u002F ✅ CORRECT: Use iCloud Drive for files\n\u002F\u002F Files automatically sync via ubiquitous container\ntry data.write(to: iCloudDocumentsURL.appendingPathComponent(\"doc.pdf\"))\n```\n\n---\n\n## Quick Reference Table\n\n| Data Type | Format | Local Location | Cloud Sync | Use Skill |\n|-----------|--------|----------------|------------|-----------|\n| User tasks, notes | Structured | Application Support | SwiftData + CloudKit | `axiom-swiftdata` → `axiom-cloudkit-ref` |\n| User photos (created) | File | Documents | iCloud Drive | `axiom-file-protection-ref` → `axiom-icloud-drive-ref` |\n| Downloaded images | File | Caches | None (re-download) | `axiom-storage-management-ref` |\n| Thumbnails | File | Caches | None (regenerate) | `axiom-storage-management-ref` |\n| Database file | File | Application Support | CKSyncEngine (if custom) | `axiom-sqlitedata` → `axiom-cloudkit-ref` |\n| Temp processing | File | tmp | None | N\u002FA |\n| User settings | Key-Value | UserDefaults | NSUbiquitousKeyValueStore | N\u002FA |\n\n---\n\n## tvOS Storage\n\n**tvOS has no persistent local storage.** This catches every iOS developer.\n\n| Directory | tvOS Behavior |\n|-----------|--------------|\n| Documents | Does not exist |\n| Application Support | System can delete when app is not running |\n| Caches | System deletes at any time |\n| tmp | System deletes at any time |\n| UserDefaults | 500 KB limit (vs ~4 MB on iOS) |\n\n**Every local file can vanish between app launches.** Your tvOS app must survive starting from zero.\n\n**Recommended**: Use iCloud (CloudKit, NSUbiquitousKeyValueStore, or iCloud Drive) as primary storage. Treat local files as cache only. See `axiom-tvos` for full tvOS storage patterns.\n\n---\n\n## Debugging: Data Missing or Not Syncing?\n\n**Files disappeared**:\n- Check if stored in Caches or tmp (system purged them)\n- Check file protection level (may be inaccessible when locked)\n- **Use skill**: `axiom-storage-diag`\n\n**Backup too large**:\n- Check if re-downloadable content is in Documents (should be in Caches)\n- Check if `isExcludedFromBackup` is set on large files\n- **Use skill**: `axiom-storage-management-ref`\n\n**Data not syncing**:\n- CloudKit: Check CKSyncEngine status, account availability\n  - **Use skill**: `axiom-cloud-sync-diag`\n- iCloud Drive: Check ubiquitous container entitlements, file coordinator\n  - **Use skill**: `axiom-icloud-drive-ref`, `axiom-cloud-sync-diag`\n\n---\n\n## Migration Checklist\n\nWhen changing storage approach:\n\n**Database to Database** (e.g., Core Data → SwiftData):\n- [ ] Create SwiftData models matching Core Data entities\n- [ ] Write migration code to copy data\n- [ ] Test with production-size datasets\n- [ ] Keep old database for rollback\n\n**Files to Database**:\n- [ ] Identify all JSON\u002Fplist files storing structured data\n- [ ] Create SwiftData models\n- [ ] Write one-time migration on first launch\n- [ ] Verify all data migrated, then delete old files\n\n**Local to Cloud**:\n- [ ] Ensure proper entitlements (CloudKit\u002FiCloud)\n- [ ] Handle initial upload carefully (bandwidth)\n- [ ] Test conflict resolution\n- [ ] Provide user control (opt-in)\n\n---\n\n**Last Updated**: 2025-12-12\n**Skill Type**: Discipline\n**Related WWDC Sessions**:\n- WWDC 2023-10187: Meet SwiftData\n- WWDC 2023-10188: Sync to iCloud with CKSyncEngine\n- WWDC 2024-10137: What's new in SwiftData\n\n",{"name":5,"license":15,"metadata":44,"description":7,"compatibility":47},{"version":45,"last-updated":46},"1.0.0","2025-12-12","iOS 17+, iPadOS 17+, macOS Sonoma+","main","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002FCharlesWiltgen\u002FAxiom\u002Faxiom-storage",24,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub","https:\u002F\u002Fskillshub.wtf","https:\u002F\u002Fgithub.com\u002FComeOnOliver","AI & Blockchain Engineer\r\n","2026-06-24 22:07:19",{"source":8,"skillName":5,"commands":58,"prompt":68,"zipAvailable":69},[59,62,65],{"tool":60,"command":61},"npx","npx skills add ComeOnOliver\u002Fskillshub --skill axiom-storage",{"tool":63,"command":64},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill axiom-storage",{"tool":66,"command":67},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill axiom-storage","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002FCharlesWiltgen\u002FAxiom\u002Faxiom-storage\u002FSKILL.md",true,[],[72,86,96,105,111,118],{"skillNo":73,"name":74,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":75,"categories":76,"keywords":82,"publishedAt":39},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[77],{"categoryNo":78,"categoryName":79,"taxonomyType":80,"taxonomyTypeDesc":81},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[83,84,85],"model","model hierarchy","model hierarchy skill",{"skillNo":87,"name":88,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":89,"categories":90,"keywords":92,"publishedAt":39},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[91],{"categoryNo":78,"categoryName":79,"taxonomyType":80,"taxonomyTypeDesc":81},[93,94,95],"MCP","zoom","zoom mcp",{"skillNo":97,"name":98,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":99,"categories":100,"keywords":101,"publishedAt":39},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[102,103,104,94],"sdk","zoom apps sdk","apps",{"skillNo":106,"name":107,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":108,"categories":109,"keywords":110,"publishedAt":39},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[107],{"skillNo":112,"name":113,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":114,"categories":115,"keywords":117,"publishedAt":39},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[116],{"categoryNo":78,"categoryName":79,"taxonomyType":80,"taxonomyTypeDesc":81},[113],{"skillNo":119,"name":120,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":121,"categories":122,"keywords":123,"publishedAt":39},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[120]]