[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6E689ADKP9X0DXPTEY23K55":3,"skill-same:01M6E689ADKP9X0DXPTEY23K55":69},{"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":23,"publishedAt":37,"linkOnly":38,"markdownContent":39,"markdownBody":40,"frontMatter":41,"refName":46,"commitSha":47,"skillDirectory":48,"forksCount":49,"repositoryHtmlUrl":50,"repositoryHomepageUrl":51,"ownerHtmlUrl":52,"ownerBlogUrl":6,"ownerDescription":53,"updatedAt":54,"install":55,"resources":68},"01M6E689ADKP9X0DXPTEY23K55","axiom-core-data",null,"Use when choosing Core Data vs SwiftData, setting up the Core Data stack, modeling relationships, or implementing concurrency patterns - prevents thread-confinement errors and migration crashes","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-core-data\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000033","数据分析师","PROFESSION","适用职业",[24,25,26,27,28,29,30,31,32,33,34,35,36],"core","data","axiom core data","macos","ios","concurrency","stack","use","up","migration","patterns","relationships","modeling","2026-11-10 10:12:30",false,"---\nname: axiom-core-data\ndescription: Use when choosing Core Data vs SwiftData, setting up the Core Data stack, modeling relationships, or implementing concurrency patterns - prevents thread-confinement errors and migration crashes\nlicense: MIT\ncompatibility: iOS 3+, macOS 10.4+\nmetadata:\n  version: \"1.0.0\"\n  last-updated: \"2025-12-25\"\n---\n\n# Core Data\n\n## Overview\n\n**Core principle**: Core Data is a mature object graph and persistence framework. Use it when needing features SwiftData doesn't support, or when targeting older iOS versions.\n\n**When to use Core Data vs SwiftData**:\n- **SwiftData** (iOS 17+) — New apps, simpler API, Swift-native\n- **Core Data** — iOS 16 and earlier, advanced features, existing codebases\n\n## Quick Decision Tree\n\n```\nWhich persistence framework?\n\n├─ Targeting iOS 17+ only?\n│  ├─ Simple data model? → SwiftData (recommended)\n│  ├─ Need public CloudKit database? → Core Data (SwiftData is private-only)\n│  ├─ Need custom migration logic? → Core Data (more control)\n│  └─ Existing Core Data app? → Keep Core Data or migrate gradually\n│\n├─ Targeting iOS 16 or earlier?\n│  └─ Core Data (SwiftData unavailable)\n│\n└─ Need both? → Use Core Data with SwiftData wrapper (advanced)\n```\n\n## Red Flags\n\nIf ANY of these appear, STOP:\n\n- ❌ \"Access managed objects on any thread\" — Thread-confinement violation\n- ❌ \"Skip migration testing on real device\" — Simulator hides schema issues\n- ❌ \"Use a singleton context everywhere\" — Leads to concurrency crashes\n- ❌ \"Force lightweight migration always\" — Complex changes need mapping models\n- ❌ \"Fetch in view body\" — Use @FetchRequest or observe in view model\n\n## Core Data Stack Setup\n\n### Modern Stack (iOS 10+)\n\n```swift\nimport CoreData\n\nclass CoreDataStack {\n    static let shared = CoreDataStack()\n\n    lazy var persistentContainer: NSPersistentContainer = {\n        let container = NSPersistentContainer(name: \"Model\")\n\n        \u002F\u002F Configure for CloudKit if needed\n        \u002F\u002F container.persistentStoreDescriptions.first?.cloudKitContainerOptions =\n        \u002F\u002F     NSPersistentCloudKitContainerOptions(containerIdentifier: \"iCloud.com.app\")\n\n        container.loadPersistentStores { description, error in\n            if let error = error {\n                \u002F\u002F Handle appropriately for production\n                fatalError(\"Failed to load store: \\(error)\")\n            }\n        }\n\n        \u002F\u002F Enable automatic merging\n        container.viewContext.automaticallyMergesChangesFromParent = true\n        container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy\n\n        return container\n    }()\n\n    var viewContext: NSManagedObjectContext {\n        persistentContainer.viewContext\n    }\n\n    func newBackgroundContext() -> NSManagedObjectContext {\n        persistentContainer.newBackgroundContext()\n    }\n}\n```\n\n### CloudKit Integration\n\n```swift\nimport CoreData\n\nclass CloudKitStack {\n    lazy var container: NSPersistentCloudKitContainer = {\n        let container = NSPersistentCloudKitContainer(name: \"Model\")\n\n        guard let description = container.persistentStoreDescriptions.first else {\n            fatalError(\"No store description\")\n        }\n\n        \u002F\u002F Enable CloudKit sync\n        description.cloudKitContainerOptions = NSPersistentCloudKitContainerOptions(\n            containerIdentifier: \"iCloud.com.yourapp\"\n        )\n\n        \u002F\u002F Enable history tracking for sync\n        description.setOption(true as NSNumber,\n                             forKey: NSPersistentHistoryTrackingKey)\n        description.setOption(true as NSNumber,\n                             forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)\n\n        container.loadPersistentStores { _, error in\n            if let error = error {\n                fatalError(\"CloudKit store failed: \\(error)\")\n            }\n        }\n\n        container.viewContext.automaticallyMergesChangesFromParent = true\n\n        return container\n    }()\n}\n```\n\n## Concurrency Patterns\n\n### The Golden Rule\n\n**NEVER pass NSManagedObject across threads.** Pass objectID instead.\n\n```swift\n\u002F\u002F ❌ WRONG: Passing object across threads\nlet user = viewContext.fetch(...)  \u002F\u002F Main thread\nTask.detached {\n    print(user.name)  \u002F\u002F CRASH: Wrong thread\n}\n\n\u002F\u002F ✅ CORRECT: Pass objectID, fetch on target context\nlet userID = user.objectID\nTask.detached {\n    let bgContext = CoreDataStack.shared.newBackgroundContext()\n    let user = bgContext.object(with: userID) as! User\n    print(user.name)  \u002F\u002F Safe\n}\n```\n\n### Background Processing\n\n```swift\n\u002F\u002F ✅ CORRECT: Background context for heavy work\nfunc importData(_ items: [ImportItem]) async throws {\n    let context = CoreDataStack.shared.newBackgroundContext()\n\n    try await context.perform {\n        for item in items {\n            let entity = Entity(context: context)\n            entity.configure(from: item)\n        }\n\n        try context.save()\n    }\n}\n\n\u002F\u002F Changes automatically merge to viewContext if configured\n```\n\n### Async\u002FAwait (iOS 15+)\n\n```swift\n\u002F\u002F Modern async context operations\nfunc fetchUsers() async throws -> [User] {\n    let context = CoreDataStack.shared.viewContext\n\n    return try await context.perform {\n        let request = User.fetchRequest()\n        request.sortDescriptors = [NSSortDescriptor(key: \"name\", ascending: true)]\n        return try context.fetch(request)\n    }\n}\n```\n\n## Relationship Modeling\n\n### One-to-Many\n\n```swift\n\u002F\u002F In User entity\n@NSManaged var posts: NSSet?\n\n\u002F\u002F Convenience accessors\nextension User {\n    var postsArray: [Post] {\n        (posts?.allObjects as? [Post]) ?? []\n    }\n\n    func addPost(_ post: Post) {\n        mutableSetValue(forKey: \"posts\").add(post)\n    }\n}\n```\n\n### Many-to-Many\n\n```swift\n\u002F\u002F Both sides have NSSet\n\u002F\u002F User.tags \u003C-> Tag.users\n\nextension User {\n    func addTag(_ tag: Tag) {\n        mutableSetValue(forKey: \"tags\").add(tag)\n        \u002F\u002F Core Data automatically adds to tag.users\n    }\n}\n```\n\n### Delete Rules\n\n| Rule | Behavior | Use Case |\n|------|----------|----------|\n| **Nullify** | Set relationship to nil | Optional relationships |\n| **Cascade** | Delete related objects | Owned children (User → Posts) |\n| **Deny** | Prevent deletion if related objects exist | Protect referenced data |\n| **No Action** | Do nothing (manual cleanup required) | Rarely appropriate |\n\n## Fetching Patterns\n\n### SwiftUI Integration\n\n```swift\nstruct UserList: View {\n    @FetchRequest(\n        sortDescriptors: [NSSortDescriptor(keyPath: \\User.name, ascending: true)],\n        predicate: NSPredicate(format: \"isActive == YES\"),\n        animation: .default\n    )\n    private var users: FetchedResults\u003CUser>\n\n    var body: some View {\n        List(users) { user in\n            Text(user.name ?? \"Unknown\")\n        }\n    }\n}\n\n\u002F\u002F Dynamic predicates\nstruct FilteredList: View {\n    @FetchRequest var items: FetchedResults\u003CItem>\n\n    init(category: String) {\n        _items = FetchRequest(\n            sortDescriptors: [NSSortDescriptor(keyPath: \\Item.date, ascending: false)],\n            predicate: NSPredicate(format: \"category == %@\", category)\n        )\n    }\n}\n```\n\n### Batch Fetching (Avoid N+1)\n\n```swift\n\u002F\u002F ❌ WRONG: N+1 queries\nlet users = try context.fetch(User.fetchRequest())\nfor user in users {\n    print(user.posts?.count ?? 0)  \u002F\u002F Fault fired for each user\n}\n\n\u002F\u002F ✅ CORRECT: Prefetch relationships\nlet request = User.fetchRequest()\nrequest.relationshipKeyPathsForPrefetching = [\"posts\"]\nlet users = try context.fetch(request)\nfor user in users {\n    print(user.posts?.count ?? 0)  \u002F\u002F Already loaded\n}\n```\n\n### Batch Size for Large Datasets\n\n```swift\nlet request = User.fetchRequest()\nrequest.fetchBatchSize = 20  \u002F\u002F Load 20 at a time as needed\nrequest.returnsObjectsAsFaults = true  \u002F\u002F Default, memory efficient\n```\n\n## Schema Migration\n\n### Lightweight Migration (Automatic)\n\nHandled automatically for:\n- Adding optional attributes\n- Removing attributes\n- Renaming (with renaming identifier)\n- Adding relationships with optional or default value\n\n```swift\nlet description = NSPersistentStoreDescription()\ndescription.shouldMigrateStoreAutomatically = true\ndescription.shouldInferMappingModelAutomatically = true\n```\n\n### When Mapping Model Is Needed\n\n- Changing attribute types\n- Splitting\u002Fmerging entities\n- Complex relationship changes\n- Data transformation during migration\n\n```swift\n\u002F\u002F Create mapping model in Xcode:\n\u002F\u002F File → New → Mapping Model\n\u002F\u002F Select source and destination models\n```\n\n### Migration Testing Checklist\n\n**MANDATORY before shipping**:\n\n1. ✓ Test on REAL DEVICE (simulator deletes DB on rebuild)\n2. ✓ Install old version, create data\n3. ✓ Install new version over it\n4. ✓ Verify all data accessible\n5. ✓ Check migration performance (large datasets)\n\n## Anti-Patterns\n\n### 1. Singleton Context for Everything\n\n```swift\n\u002F\u002F ❌ WRONG: One context for all operations\nclass DataManager {\n    let context = CoreDataStack.shared.viewContext\n\n    func importInBackground() {\n        \u002F\u002F Using main context on background = crash\n        for item in largeDataset {\n            let entity = Entity(context: context)\n        }\n    }\n}\n\n\u002F\u002F ✅ CORRECT: Context per operation type\nfunc importInBackground() {\n    let bgContext = CoreDataStack.shared.newBackgroundContext()\n    bgContext.perform {\n        \u002F\u002F Safe background work\n    }\n}\n```\n\n### 2. Fetching in View Body\n\n```swift\n\u002F\u002F ❌ WRONG: Fetch on every render\nvar body: some View {\n    let users = try? context.fetch(User.fetchRequest())  \u002F\u002F Called repeatedly!\n    List(users ?? []) { ... }\n}\n\n\u002F\u002F ✅ CORRECT: Use @FetchRequest\n@FetchRequest(sortDescriptors: [])\nvar users: FetchedResults\u003CUser>\n\nvar body: some View {\n    List(users) { ... }  \u002F\u002F Automatic updates\n}\n```\n\n### 3. Ignoring Merge Policy\n\n```swift\n\u002F\u002F ❌ WRONG: No merge policy (conflicts crash)\nlet context = container.viewContext\n\n\u002F\u002F ✅ CORRECT: Define merge behavior\ncontext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy\ncontext.automaticallyMergesChangesFromParent = true\n```\n\n## Performance Tips\n\n1. **Use fetchBatchSize** for large result sets\n2. **Prefetch relationships** that will be accessed\n3. **Use background contexts** for imports\u002Fexports\n4. **Batch save** — don't save after each insert\n5. **Use fetchLimit** when only first N results are needed\n6. **Profile with SQL debug**: `-com.apple.CoreData.SQLDebug 1`\n\n## Pressure Scenarios\n\n### Scenario 1: \"SwiftData is simpler, let's migrate now\"\n\n**Situation**: New iOS 17 features available, temptation to migrate mid-project.\n\n**Risk**: Migration is complex. Mixed Core Data + SwiftData has sharp edges.\n\n**Response**: \"Complete current milestone first. Migration needs dedicated time and testing.\"\n\n### Scenario 2: \"Skip migration testing, simulator works\"\n\n**Situation**: Schema change tested only in simulator.\n\n**Risk**: Simulator deletes database on rebuild. Real devices keep persistent data and crash.\n\n**Response**: \"MANDATORY: Test on real device with real data. 15 minutes now prevents production crash.\"\n\n## tvOS\n\n**CoreData + CloudKit is dangerous on tvOS.** CloudKit metadata causes significant space inflation in the local store, and tvOS has no persistent local storage — the system deletes Caches (including Application Support) at any time. The inflated store plus random deletion is a worst-case combination.\n\n**Recommendation**: Use SQLiteData with CloudKit SyncEngine instead for tvOS data persistence. See `axiom-tvos` for full tvOS storage constraints.\n\n## Related Skills\n\n- `axiom-core-data-diag` — Debugging migrations, thread errors, N+1 queries\n- `axiom-swiftdata` — Modern alternative for iOS 17+\n- `axiom-database-migration` — Safe schema evolution patterns\n- `axiom-swift-concurrency` — Async\u002Fawait patterns for Core Data\n\n","# Core Data\n\n## Overview\n\n**Core principle**: Core Data is a mature object graph and persistence framework. Use it when needing features SwiftData doesn't support, or when targeting older iOS versions.\n\n**When to use Core Data vs SwiftData**:\n- **SwiftData** (iOS 17+) — New apps, simpler API, Swift-native\n- **Core Data** — iOS 16 and earlier, advanced features, existing codebases\n\n## Quick Decision Tree\n\n```\nWhich persistence framework?\n\n├─ Targeting iOS 17+ only?\n│  ├─ Simple data model? → SwiftData (recommended)\n│  ├─ Need public CloudKit database? → Core Data (SwiftData is private-only)\n│  ├─ Need custom migration logic? → Core Data (more control)\n│  └─ Existing Core Data app? → Keep Core Data or migrate gradually\n│\n├─ Targeting iOS 16 or earlier?\n│  └─ Core Data (SwiftData unavailable)\n│\n└─ Need both? → Use Core Data with SwiftData wrapper (advanced)\n```\n\n## Red Flags\n\nIf ANY of these appear, STOP:\n\n- ❌ \"Access managed objects on any thread\" — Thread-confinement violation\n- ❌ \"Skip migration testing on real device\" — Simulator hides schema issues\n- ❌ \"Use a singleton context everywhere\" — Leads to concurrency crashes\n- ❌ \"Force lightweight migration always\" — Complex changes need mapping models\n- ❌ \"Fetch in view body\" — Use @FetchRequest or observe in view model\n\n## Core Data Stack Setup\n\n### Modern Stack (iOS 10+)\n\n```swift\nimport CoreData\n\nclass CoreDataStack {\n    static let shared = CoreDataStack()\n\n    lazy var persistentContainer: NSPersistentContainer = {\n        let container = NSPersistentContainer(name: \"Model\")\n\n        \u002F\u002F Configure for CloudKit if needed\n        \u002F\u002F container.persistentStoreDescriptions.first?.cloudKitContainerOptions =\n        \u002F\u002F     NSPersistentCloudKitContainerOptions(containerIdentifier: \"iCloud.com.app\")\n\n        container.loadPersistentStores { description, error in\n            if let error = error {\n                \u002F\u002F Handle appropriately for production\n                fatalError(\"Failed to load store: \\(error)\")\n            }\n        }\n\n        \u002F\u002F Enable automatic merging\n        container.viewContext.automaticallyMergesChangesFromParent = true\n        container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy\n\n        return container\n    }()\n\n    var viewContext: NSManagedObjectContext {\n        persistentContainer.viewContext\n    }\n\n    func newBackgroundContext() -> NSManagedObjectContext {\n        persistentContainer.newBackgroundContext()\n    }\n}\n```\n\n### CloudKit Integration\n\n```swift\nimport CoreData\n\nclass CloudKitStack {\n    lazy var container: NSPersistentCloudKitContainer = {\n        let container = NSPersistentCloudKitContainer(name: \"Model\")\n\n        guard let description = container.persistentStoreDescriptions.first else {\n            fatalError(\"No store description\")\n        }\n\n        \u002F\u002F Enable CloudKit sync\n        description.cloudKitContainerOptions = NSPersistentCloudKitContainerOptions(\n            containerIdentifier: \"iCloud.com.yourapp\"\n        )\n\n        \u002F\u002F Enable history tracking for sync\n        description.setOption(true as NSNumber,\n                             forKey: NSPersistentHistoryTrackingKey)\n        description.setOption(true as NSNumber,\n                             forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)\n\n        container.loadPersistentStores { _, error in\n            if let error = error {\n                fatalError(\"CloudKit store failed: \\(error)\")\n            }\n        }\n\n        container.viewContext.automaticallyMergesChangesFromParent = true\n\n        return container\n    }()\n}\n```\n\n## Concurrency Patterns\n\n### The Golden Rule\n\n**NEVER pass NSManagedObject across threads.** Pass objectID instead.\n\n```swift\n\u002F\u002F ❌ WRONG: Passing object across threads\nlet user = viewContext.fetch(...)  \u002F\u002F Main thread\nTask.detached {\n    print(user.name)  \u002F\u002F CRASH: Wrong thread\n}\n\n\u002F\u002F ✅ CORRECT: Pass objectID, fetch on target context\nlet userID = user.objectID\nTask.detached {\n    let bgContext = CoreDataStack.shared.newBackgroundContext()\n    let user = bgContext.object(with: userID) as! User\n    print(user.name)  \u002F\u002F Safe\n}\n```\n\n### Background Processing\n\n```swift\n\u002F\u002F ✅ CORRECT: Background context for heavy work\nfunc importData(_ items: [ImportItem]) async throws {\n    let context = CoreDataStack.shared.newBackgroundContext()\n\n    try await context.perform {\n        for item in items {\n            let entity = Entity(context: context)\n            entity.configure(from: item)\n        }\n\n        try context.save()\n    }\n}\n\n\u002F\u002F Changes automatically merge to viewContext if configured\n```\n\n### Async\u002FAwait (iOS 15+)\n\n```swift\n\u002F\u002F Modern async context operations\nfunc fetchUsers() async throws -> [User] {\n    let context = CoreDataStack.shared.viewContext\n\n    return try await context.perform {\n        let request = User.fetchRequest()\n        request.sortDescriptors = [NSSortDescriptor(key: \"name\", ascending: true)]\n        return try context.fetch(request)\n    }\n}\n```\n\n## Relationship Modeling\n\n### One-to-Many\n\n```swift\n\u002F\u002F In User entity\n@NSManaged var posts: NSSet?\n\n\u002F\u002F Convenience accessors\nextension User {\n    var postsArray: [Post] {\n        (posts?.allObjects as? [Post]) ?? []\n    }\n\n    func addPost(_ post: Post) {\n        mutableSetValue(forKey: \"posts\").add(post)\n    }\n}\n```\n\n### Many-to-Many\n\n```swift\n\u002F\u002F Both sides have NSSet\n\u002F\u002F User.tags \u003C-> Tag.users\n\nextension User {\n    func addTag(_ tag: Tag) {\n        mutableSetValue(forKey: \"tags\").add(tag)\n        \u002F\u002F Core Data automatically adds to tag.users\n    }\n}\n```\n\n### Delete Rules\n\n| Rule | Behavior | Use Case |\n|------|----------|----------|\n| **Nullify** | Set relationship to nil | Optional relationships |\n| **Cascade** | Delete related objects | Owned children (User → Posts) |\n| **Deny** | Prevent deletion if related objects exist | Protect referenced data |\n| **No Action** | Do nothing (manual cleanup required) | Rarely appropriate |\n\n## Fetching Patterns\n\n### SwiftUI Integration\n\n```swift\nstruct UserList: View {\n    @FetchRequest(\n        sortDescriptors: [NSSortDescriptor(keyPath: \\User.name, ascending: true)],\n        predicate: NSPredicate(format: \"isActive == YES\"),\n        animation: .default\n    )\n    private var users: FetchedResults\u003CUser>\n\n    var body: some View {\n        List(users) { user in\n            Text(user.name ?? \"Unknown\")\n        }\n    }\n}\n\n\u002F\u002F Dynamic predicates\nstruct FilteredList: View {\n    @FetchRequest var items: FetchedResults\u003CItem>\n\n    init(category: String) {\n        _items = FetchRequest(\n            sortDescriptors: [NSSortDescriptor(keyPath: \\Item.date, ascending: false)],\n            predicate: NSPredicate(format: \"category == %@\", category)\n        )\n    }\n}\n```\n\n### Batch Fetching (Avoid N+1)\n\n```swift\n\u002F\u002F ❌ WRONG: N+1 queries\nlet users = try context.fetch(User.fetchRequest())\nfor user in users {\n    print(user.posts?.count ?? 0)  \u002F\u002F Fault fired for each user\n}\n\n\u002F\u002F ✅ CORRECT: Prefetch relationships\nlet request = User.fetchRequest()\nrequest.relationshipKeyPathsForPrefetching = [\"posts\"]\nlet users = try context.fetch(request)\nfor user in users {\n    print(user.posts?.count ?? 0)  \u002F\u002F Already loaded\n}\n```\n\n### Batch Size for Large Datasets\n\n```swift\nlet request = User.fetchRequest()\nrequest.fetchBatchSize = 20  \u002F\u002F Load 20 at a time as needed\nrequest.returnsObjectsAsFaults = true  \u002F\u002F Default, memory efficient\n```\n\n## Schema Migration\n\n### Lightweight Migration (Automatic)\n\nHandled automatically for:\n- Adding optional attributes\n- Removing attributes\n- Renaming (with renaming identifier)\n- Adding relationships with optional or default value\n\n```swift\nlet description = NSPersistentStoreDescription()\ndescription.shouldMigrateStoreAutomatically = true\ndescription.shouldInferMappingModelAutomatically = true\n```\n\n### When Mapping Model Is Needed\n\n- Changing attribute types\n- Splitting\u002Fmerging entities\n- Complex relationship changes\n- Data transformation during migration\n\n```swift\n\u002F\u002F Create mapping model in Xcode:\n\u002F\u002F File → New → Mapping Model\n\u002F\u002F Select source and destination models\n```\n\n### Migration Testing Checklist\n\n**MANDATORY before shipping**:\n\n1. ✓ Test on REAL DEVICE (simulator deletes DB on rebuild)\n2. ✓ Install old version, create data\n3. ✓ Install new version over it\n4. ✓ Verify all data accessible\n5. ✓ Check migration performance (large datasets)\n\n## Anti-Patterns\n\n### 1. Singleton Context for Everything\n\n```swift\n\u002F\u002F ❌ WRONG: One context for all operations\nclass DataManager {\n    let context = CoreDataStack.shared.viewContext\n\n    func importInBackground() {\n        \u002F\u002F Using main context on background = crash\n        for item in largeDataset {\n            let entity = Entity(context: context)\n        }\n    }\n}\n\n\u002F\u002F ✅ CORRECT: Context per operation type\nfunc importInBackground() {\n    let bgContext = CoreDataStack.shared.newBackgroundContext()\n    bgContext.perform {\n        \u002F\u002F Safe background work\n    }\n}\n```\n\n### 2. Fetching in View Body\n\n```swift\n\u002F\u002F ❌ WRONG: Fetch on every render\nvar body: some View {\n    let users = try? context.fetch(User.fetchRequest())  \u002F\u002F Called repeatedly!\n    List(users ?? []) { ... }\n}\n\n\u002F\u002F ✅ CORRECT: Use @FetchRequest\n@FetchRequest(sortDescriptors: [])\nvar users: FetchedResults\u003CUser>\n\nvar body: some View {\n    List(users) { ... }  \u002F\u002F Automatic updates\n}\n```\n\n### 3. Ignoring Merge Policy\n\n```swift\n\u002F\u002F ❌ WRONG: No merge policy (conflicts crash)\nlet context = container.viewContext\n\n\u002F\u002F ✅ CORRECT: Define merge behavior\ncontext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy\ncontext.automaticallyMergesChangesFromParent = true\n```\n\n## Performance Tips\n\n1. **Use fetchBatchSize** for large result sets\n2. **Prefetch relationships** that will be accessed\n3. **Use background contexts** for imports\u002Fexports\n4. **Batch save** — don't save after each insert\n5. **Use fetchLimit** when only first N results are needed\n6. **Profile with SQL debug**: `-com.apple.CoreData.SQLDebug 1`\n\n## Pressure Scenarios\n\n### Scenario 1: \"SwiftData is simpler, let's migrate now\"\n\n**Situation**: New iOS 17 features available, temptation to migrate mid-project.\n\n**Risk**: Migration is complex. Mixed Core Data + SwiftData has sharp edges.\n\n**Response**: \"Complete current milestone first. Migration needs dedicated time and testing.\"\n\n### Scenario 2: \"Skip migration testing, simulator works\"\n\n**Situation**: Schema change tested only in simulator.\n\n**Risk**: Simulator deletes database on rebuild. Real devices keep persistent data and crash.\n\n**Response**: \"MANDATORY: Test on real device with real data. 15 minutes now prevents production crash.\"\n\n## tvOS\n\n**CoreData + CloudKit is dangerous on tvOS.** CloudKit metadata causes significant space inflation in the local store, and tvOS has no persistent local storage — the system deletes Caches (including Application Support) at any time. The inflated store plus random deletion is a worst-case combination.\n\n**Recommendation**: Use SQLiteData with CloudKit SyncEngine instead for tvOS data persistence. See `axiom-tvos` for full tvOS storage constraints.\n\n## Related Skills\n\n- `axiom-core-data-diag` — Debugging migrations, thread errors, N+1 queries\n- `axiom-swiftdata` — Modern alternative for iOS 17+\n- `axiom-database-migration` — Safe schema evolution patterns\n- `axiom-swift-concurrency` — Async\u002Fawait patterns for Core Data\n\n",{"name":5,"license":15,"metadata":42,"description":7,"compatibility":45},{"version":43,"last-updated":44},"1.0.0","2025-12-25","iOS 3+, macOS 10.4+","main","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002FCharlesWiltgen\u002FAxiom\u002Faxiom-core-data",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":56,"prompt":66,"zipAvailable":67},[57,60,63],{"tool":58,"command":59},"npx","npx skills add ComeOnOliver\u002Fskillshub --skill axiom-core-data",{"tool":61,"command":62},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill axiom-core-data",{"tool":64,"command":65},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill axiom-core-data","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002FCharlesWiltgen\u002FAxiom\u002Faxiom-core-data\u002FSKILL.md",true,[],[70,84,94,103,109,116],{"skillNo":71,"name":72,"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":73,"categories":74,"keywords":80,"publishedAt":37},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[75],{"categoryNo":76,"categoryName":77,"taxonomyType":78,"taxonomyTypeDesc":79},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[81,82,83],"model","model hierarchy","model hierarchy skill",{"skillNo":85,"name":86,"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":87,"categories":88,"keywords":90,"publishedAt":37},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[89],{"categoryNo":76,"categoryName":77,"taxonomyType":78,"taxonomyTypeDesc":79},[91,92,93],"MCP","zoom","zoom mcp",{"skillNo":95,"name":96,"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":97,"categories":98,"keywords":99,"publishedAt":37},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[100,101,102,92],"sdk","zoom apps sdk","apps",{"skillNo":104,"name":105,"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":106,"categories":107,"keywords":108,"publishedAt":37},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[105],{"skillNo":110,"name":111,"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":112,"categories":113,"keywords":115,"publishedAt":37},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[114],{"categoryNo":76,"categoryName":77,"taxonomyType":78,"taxonomyTypeDesc":79},[111],{"skillNo":117,"name":118,"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":119,"categories":120,"keywords":121,"publishedAt":37},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[118]]