{"id":"015bc6f6-b1b4-432d-97a8-845773d57971","shortId":"QTKWrf","kind":"skill","title":"core-data","tagline":"Build, review, or improve Core Data persistence in apps that have not adopted SwiftData. Use when working with NSManagedObject subclasses, NSFetchedResultsController for list-driven UI, NSBatchInsertRequest / NSBatchDeleteRequest / NSBatchUpdateRequest for bulk operations, NSPers","description":"# Core Data\n\nBuild and maintain data persistence using Core Data for apps that have not\nadopted SwiftData. Covers stack setup, concurrency, batch operations,\nNSFetchedResultsController, persistent history tracking, staged migration,\nand testing.\n\n## Contents\n\n- [Stack Setup](#stack-setup)\n- [Concurrency and Threading](#concurrency-and-threading)\n- [NSFetchedResultsController](#nsfetchedresultscontroller)\n- [Batch Operations](#batch-operations)\n- [Persistent History Tracking](#persistent-history-tracking)\n- [Staged Migration](#staged-migration)\n- [Composite Attributes](#composite-attributes)\n- [Testing](#testing)\n- [Common Mistakes](#common-mistakes)\n- [Review Checklist](#review-checklist)\n- [References](#references)\n\n## Stack Setup\n\n`NSPersistentContainer` encapsulates the Core Data stack.\n\nDocs: [NSPersistentContainer](https://sosumi.ai/documentation/coredata/nspersistentcontainer)\n\n```swift\nimport CoreData\n\nfinal class CoreDataStack: @unchecked Sendable {\n    static let shared = CoreDataStack()\n\n    let container: NSPersistentContainer\n\n    private init() {\n        container = NSPersistentContainer(name: \"MyAppModel\")\n        container.loadPersistentStores { _, error in\n            if let error { fatalError(\"Core Data store failed: \\(error)\") }\n        }\n        container.viewContext.automaticallyMergesChangesFromParent = true\n        container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy\n    }\n\n    var viewContext: NSManagedObjectContext { container.viewContext }\n\n    func newBackgroundContext() -> NSManagedObjectContext {\n        container.newBackgroundContext()\n    }\n}\n```\n\nFor CloudKit sync, use `NSPersistentCloudKitContainer` instead.\n\n## Concurrency and Threading\n\nCore Data contexts are bound to queues. The `viewContext` is on the main queue;\nbackground contexts operate on private queues.\n\nDocs: [NSManagedObjectContext](https://sosumi.ai/documentation/coredata/nsmanagedobjectcontext)\n\n**Rules:**\n- Always use `perform(_:)` or `performAndWait(_:)` when accessing a context\n  off its own queue.\n- Never pass `NSManagedObject` instances across context or thread boundaries.\n  Pass `NSManagedObjectID` instead and re-fetch.\n- Set `automaticallyMergesChangesFromParent = true` on the `viewContext`.\n\n```swift\n// Writing on a background context\nfunc updateTrip(id: NSManagedObjectID, newName: String) async throws {\n    let context = CoreDataStack.shared.newBackgroundContext()\n    try await context.perform {\n        guard let trip = try context.existingObject(with: id) as? CDTrip else {\n            throw PersistenceError.notFound\n        }\n        trip.name = newName\n        try context.save()\n    }\n}\n```\n\n### Swift Concurrency Integration\n\n`NSManagedObjectContext.perform(_:)` has an `async throws` overload\n(iOS 15+). Avoid marking `NSManagedObject` subclasses as `Sendable`.\n\n```swift\nfunc importItems(_ records: [ItemRecord]) async throws {\n    let context = CoreDataStack.shared.newBackgroundContext()\n    try await context.perform {\n        for record in records {\n            let item = CDItem(context: context)\n            item.id = record.id\n            item.title = record.title\n        }\n        try context.save()\n    }\n    // After save completes, viewContext auto-merges if configured\n}\n```\n\n**Do not use `@unchecked Sendable` on managed objects.** If you need\ncross-boundary communication, pass the `objectID` (which is `Sendable`)\nand re-fetch:\n\n```swift\nlet objectID = trip.objectID  // Sendable\nTask.detached {\n    let bgContext = CoreDataStack.shared.newBackgroundContext()\n    try await bgContext.perform {\n        let trip = try bgContext.existingObject(with: objectID) as! CDTrip\n        trip.isFavorite = true\n        try bgContext.save()\n    }\n}\n```\n\n## NSFetchedResultsController\n\nEfficiently drives `UITableView` / `UICollectionView` from a Core Data fetch\nrequest, with built-in change tracking and optional caching.\n\nDocs: [NSFetchedResultsController](https://sosumi.ai/documentation/coredata/nsfetchedresultscontroller)\n\n```swift\nimport CoreData\nimport UIKit\n\nclass TripsViewController: UITableViewController, NSFetchedResultsControllerDelegate {\n\n    private lazy var fetchedResultsController: NSFetchedResultsController<CDTrip> = {\n        let request: NSFetchRequest<CDTrip> = CDTrip.fetchRequest()\n        request.sortDescriptors = [\n            NSSortDescriptor(keyPath: \\CDTrip.startDate, ascending: false)\n        ]\n        request.fetchBatchSize = 20\n\n        let controller = NSFetchedResultsController(\n            fetchRequest: request,\n            managedObjectContext: CoreDataStack.shared.viewContext,\n            sectionNameKeyPath: nil,\n            cacheName: \"TripsCache\"\n        )\n        controller.delegate = self\n        return controller\n    }()\n\n    override func viewDidLoad() {\n        super.viewDidLoad()\n        try? fetchedResultsController.performFetch()\n    }\n\n    // MARK: - UITableViewDataSource\n\n    override func numberOfSections(in tableView: UITableView) -> Int {\n        fetchedResultsController.sections?.count ?? 0\n    }\n\n    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {\n        fetchedResultsController.sections?[section].numberOfObjects ?? 0\n    }\n\n    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {\n        let cell = tableView.dequeueReusableCell(withIdentifier: \"TripCell\", for: indexPath)\n        let trip = fetchedResultsController.object(at: indexPath)\n        cell.textLabel?.text = trip.name\n        return cell\n    }\n\n    // MARK: - NSFetchedResultsControllerDelegate (diffable)\n\n    func controller(\n        _ controller: NSFetchedResultsController<any NSFetchRequestResult>,\n        didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference\n    ) {\n        let snapshot = snapshot as NSDiffableDataSourceSnapshot<String, NSManagedObjectID>\n        dataSource.apply(snapshot, animatingDifferences: true)\n    }\n}\n```\n\n**Key points:**\n- The fetch request **must** have at least one sort descriptor.\n- Call `deleteCache(withName:)` before changing the fetch request predicate or\n  sort descriptors, or set `cacheName` to `nil`.\n- The diffable snapshot delegate method (`didChangeContentWith:`) is available\n  iOS 13+ and is preferred over the older per-change callbacks.\n- After a context `reset()`, call `performFetch()` again.\n\n## Batch Operations\n\nBatch operations execute at the SQL level, bypassing the managed object\ncontext. They are fast but don't trigger context notifications automatically.\n\n### NSBatchInsertRequest (iOS 13+)\n\nDocs: [NSBatchInsertRequest](https://sosumi.ai/documentation/coredata/nsbatchinsertrequest)\n\n```swift\nfunc batchImport(_ records: [[String: Any]]) async throws {\n    let context = CoreDataStack.shared.newBackgroundContext()\n    try await context.perform {\n        let request = NSBatchInsertRequest(\n            entity: CDTrip.entity(),\n            objects: records\n        )\n        request.resultType = .objectIDs\n        let result = try context.execute(request) as? NSBatchInsertResult\n        if let ids = result?.result as? [NSManagedObjectID] {\n            NSManagedObjectContext.mergeChanges(\n                fromRemoteContextSave: [NSInsertedObjectsKey: ids],\n                into: [CoreDataStack.shared.viewContext]\n            )\n        }\n    }\n}\n```\n\n### NSBatchDeleteRequest (iOS 9+)\n\nDocs: [NSBatchDeleteRequest](https://sosumi.ai/documentation/coredata/nsbatchdeleterequest)\n\n```swift\nfunc deleteOldTrips(before cutoff: Date) async throws {\n    let context = CoreDataStack.shared.newBackgroundContext()\n    try await context.perform {\n        let fetchRequest: NSFetchRequest<NSFetchRequestResult> = CDTrip.fetchRequest()\n        fetchRequest.predicate = NSPredicate(format: \"endDate < %@\", cutoff as NSDate)\n        let request = NSBatchDeleteRequest(fetchRequest: fetchRequest)\n        request.resultType = .resultTypeObjectIDs\n        let result = try context.execute(request) as? NSBatchDeleteResult\n        if let ids = result?.result as? [NSManagedObjectID] {\n            NSManagedObjectContext.mergeChanges(\n                fromRemoteContextSave: [NSDeletedObjectsKey: ids],\n                into: [CoreDataStack.shared.viewContext]\n            )\n        }\n    }\n}\n```\n\n### NSBatchUpdateRequest (iOS 8+)\n\n```swift\nfunc markAllTripsAsNotFavorite() async throws {\n    let context = CoreDataStack.shared.newBackgroundContext()\n    try await context.perform {\n        let request = NSBatchUpdateRequest(entity: CDTrip.entity())\n        request.propertiesToUpdate = [\"isFavorite\": false]\n        request.resultType = .updatedObjectIDsResultType\n        let result = try context.execute(request) as? NSBatchUpdateResult\n        if let ids = result?.result as? [NSManagedObjectID] {\n            NSManagedObjectContext.mergeChanges(\n                fromRemoteContextSave: [NSUpdatedObjectsKey: ids],\n                into: [CoreDataStack.shared.viewContext]\n            )\n        }\n    }\n}\n```\n\n**Always merge changes** back into relevant contexts after batch operations.\nBatch delete does not enforce the Deny delete rule.\n\n## Persistent History Tracking\n\nTrack store-level changes across targets (app, extensions, widgets) and\nprocesses.\n\nDocs: [NSPersistentHistoryChangeRequest](https://sosumi.ai/documentation/coredata/nspersistenthistorychangerequest)\n\n### Enable History Tracking\n\n```swift\nlet description = NSPersistentStoreDescription()\ndescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)\ndescription.setOption(true as NSNumber,\n    forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)\ncontainer.persistentStoreDescriptions = [description]\n```\n\n### Observe, Fetch, Merge, and Purge\n\n```swift\n// 1. Observe remote change notifications\nNotificationCenter.default.addObserver(\n    self, selector: #selector(storeRemoteChange(_:)),\n    name: .NSPersistentStoreRemoteChange, object: container.persistentStoreCoordinator\n)\n\n// 2. Fetch history since last token\n@objc func storeRemoteChange(_ notification: Notification) {\n    let context = container.newBackgroundContext()\n    context.perform {\n        let request = NSPersistentHistoryChangeRequest.fetchHistory(after: self.lastToken)\n        if let result = try? context.execute(request) as? NSPersistentHistoryResult,\n           let transactions = result.result as? [NSPersistentHistoryTransaction] {\n            // 3. Merge into viewContext\n            for transaction in transactions {\n                self.container.viewContext.mergeChanges(fromContextDidSave: transaction.objectIDNotification())\n                self.lastToken = transaction.token\n            }\n        }\n        // 4. Purge old history\n        let purgeRequest = NSPersistentHistoryChangeRequest.deleteHistory(before: self.lastToken)\n        try? context.execute(purgeRequest)\n    }\n}\n```\n\nStore `lastToken` in `UserDefaults` (per target) so history is processed\ncorrectly across launches.\n\n## Staged Migration\n\n`NSStagedMigrationManager` (iOS 17+) sequences schema migrations through\nordered stages, each lightweight or custom.\n\nDocs: [NSStagedMigrationManager](https://sosumi.ai/documentation/coredata/nsstagedmigrationmanager)\n\n```swift\nimport CoreData\n\n// Define migration stages\n// Use version checksums from the compiled model versions, not model names.\nlet checksumV1 = \"<ModelV1 version checksum>\"\nlet checksumV2 = \"<ModelV2 version checksum>\"\nlet stage1to2 = NSLightweightMigrationStage([checksumV1, checksumV2])\nstage1to2.label = \"Add isFavorite property\"\n\nlet stage2to3 = NSCustomMigrationStage(\n    migratingFrom: NSManagedObjectModelReference(\n        named: \"ModelV2\", in: Bundle.main),\n    to: NSManagedObjectModelReference(\n        named: \"ModelV3\", in: Bundle.main)\n)\nstage2to3.label = \"Split name into firstName/lastName\"\nstage2to3.willMigrateHandler = { migrationManager, currentStage in\n    guard let container = migrationManager.container else { return }\n    let context = container.newBackgroundContext()\n    try context.performAndWait {\n        // Transform data between schema versions\n        let request = NSFetchRequest<NSManagedObject>(entityName: \"Person\")\n        let people = try context.fetch(request)\n        for person in people {\n            let fullName = person.value(forKey: \"name\") as? String ?? \"\"\n            let parts = fullName.split(separator: \" \", maxSplits: 1)\n            person.setValue(String(parts.first ?? \"\"), forKey: \"firstName\")\n            person.setValue(parts.count > 1 ? String(parts.last!) : \"\", forKey: \"lastName\")\n        }\n        try context.save()\n    }\n}\n\n// Apply to the persistent store\nlet manager = NSStagedMigrationManager([stage1to2, stage2to3])\nlet description = NSPersistentStoreDescription()\ndescription.setOption(manager,\n    forKey: NSPersistentStoreStagedMigrationManagerOptionKey)\ncontainer.persistentStoreDescriptions = [description]\ncontainer.loadPersistentStores { _, error in\n    if let error { fatalError(\"Migration failed: \\(error)\") }\n}\n```\n\nFor apps targeting below iOS 17, use lightweight migration\n(`NSInferMappingModelAutomaticallyOption`) or mapping models.\n\n`NSLightweightMigrationStage` takes **version checksums** (`[String]`), not\nhuman-readable model names.\n\n## Composite Attributes\n\niOS 17+ supports composite attributes: groups of sub-attributes on an entity\nthat act as a single logical unit. Define them in the model editor by adding a\nComposite type attribute and nesting sub-attributes beneath it.\n\nDocs: [NSCompositeAttributeDescription](https://sosumi.ai/documentation/coredata/nscompositeattributedescription)\n\nComposite attributes map to `Codable` structs in SwiftData coexistence\nscenarios.\n\n## Testing\n\n### In-Memory Store for Tests\n\n```swift\nimport CoreData\nimport Testing\n\nstruct CoreDataTests {\n    func makeTestContainer() throws -> NSPersistentContainer {\n        let container = NSPersistentContainer(name: \"MyAppModel\")\n        let description = NSPersistentStoreDescription()\n        description.type = NSInMemoryStoreType\n        container.persistentStoreDescriptions = [description]\n\n        var loadError: Error?\n        container.loadPersistentStores { _, error in loadError = error }\n        if let loadError { throw loadError }\n        return container\n    }\n\n    @Test func createAndFetchTrip() throws {\n        let container = try makeTestContainer()\n        let context = container.viewContext\n\n        let trip = CDTrip(context: context)\n        trip.name = \"Test Trip\"\n        trip.startDate = .now\n        try context.save()\n\n        let request: NSFetchRequest<CDTrip> = CDTrip.fetchRequest()\n        let trips = try context.fetch(request)\n        #expect(trips.count == 1)\n        #expect(trips.first?.name == \"Test Trip\")\n    }\n}\n```\n\n**Tips:**\n- Share the `NSManagedObjectModel` instance across tests to avoid \"duplicate\n  entity\" warnings.\n- Use a single shared model loaded once:\n\n```swift\nprivate let sharedModel: NSManagedObjectModel = {\n    let url = Bundle.main.url(forResource: \"MyAppModel\", withExtension: \"momd\")!\n    return NSManagedObjectModel(contentsOf: url)!\n}()\n\nfunc makeTestContainer() throws -> NSPersistentContainer {\n    let container = NSPersistentContainer(name: \"MyAppModel\",\n                                          managedObjectModel: sharedModel)\n    // ... configure in-memory store\n}\n```\n\n## Common Mistakes\n\n| Mistake | Fix |\n|---------|-----|\n| Passing `NSManagedObject` across threads | Pass `objectID` and re-fetch in the target context |\n| Forgetting to merge batch operation results | Call `mergeChanges(fromRemoteContextSave:into:)` |\n| Calling `save()` without checking `hasChanges` | Guard with `context.hasChanges` first |\n| Using deprecated `init(concurrencyType:)` confinement type | Use `.privateQueueConcurrencyType` or `.mainQueueConcurrencyType` |\n| Not setting `mergePolicy` on `viewContext` | Set `NSMergeByPropertyObjectTrumpMergePolicy` to avoid conflict crashes |\n| Modifying fetch request on live `NSFetchedResultsController` without deleting cache | Call `deleteCache(withName:)` first or use `cacheName: nil` |\n| Batch delete ignoring Deny delete rule | Batch delete bypasses delete rules; validate manually |\n| Marking `NSManagedObject` as `@unchecked Sendable` | Do not. Pass `objectID` instead |\n\n## Review Checklist\n\n- [ ] `NSPersistentContainer` is initialized once and shared\n- [ ] `viewContext` used only on main queue; background contexts for writes\n- [ ] `perform(_:)` or `performAndWait(_:)` wraps all off-queue context access\n- [ ] `automaticallyMergesChangesFromParent` set on `viewContext`\n- [ ] `mergePolicy` set on `viewContext` to prevent conflict crashes\n- [ ] Batch operation results merged into relevant contexts\n- [ ] `NSFetchedResultsController` fetch requests have sort descriptors\n- [ ] Persistent history tracking enabled for multi-target apps\n- [ ] Tests use in-memory stores with shared `NSManagedObjectModel`\n- [ ] No `NSManagedObject` instances cross thread boundaries\n\n## References\n\n- Apple docs: [Core Data](https://sosumi.ai/documentation/coredata) | [NSPersistentContainer](https://sosumi.ai/documentation/coredata/nspersistentcontainer) | [NSFetchedResultsController](https://sosumi.ai/documentation/coredata/nsfetchedresultscontroller) | [NSStagedMigrationManager](https://sosumi.ai/documentation/coredata/nsstagedmigrationmanager)","tags":["core","data","swift","ios","skills","dpearson2699","accessibility","agent-skills","ai-coding","apple","claude-code","codex-skills"],"capabilities":["skill","source-dpearson2699","skill-core-data","topic-accessibility","topic-agent-skills","topic-ai-coding","topic-apple","topic-claude-code","topic-codex-skills","topic-cursor-skills","topic-ios","topic-ios-development","topic-liquid-glass","topic-localization","topic-mapkit"],"categories":["swift-ios-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/dpearson2699/swift-ios-skills/core-data","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add dpearson2699/swift-ios-skills","source_repo":"https://github.com/dpearson2699/swift-ios-skills","install_from":"skills.sh"}},"qualityScore":"0.700","qualityRationale":"deterministic score 0.70 from registry signals: · indexed on github topic:agent-skills · 599 github stars · SKILL.md body (17,324 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T18:53:40.610Z","embedding":null,"createdAt":"2026-04-23T00:53:22.187Z","updatedAt":"2026-05-18T18:53:40.610Z","lastSeenAt":"2026-05-18T18:53:40.610Z","tsv":"'/documentation/coredata)':1474 '/documentation/coredata/nsbatchdeleterequest)':668 '/documentation/coredata/nsbatchinsertrequest)':617 '/documentation/coredata/nscompositeattributedescription)':1145 '/documentation/coredata/nsfetchedresultscontroller)':410,1482 '/documentation/coredata/nsmanagedobjectcontext)':210 '/documentation/coredata/nspersistentcontainer)':131,1478 '/documentation/coredata/nspersistenthistorychangerequest)':803 '/documentation/coredata/nsstagedmigrationmanager)':935,1486 '0':469,482 '1':831,1032,1040,1235 '13':568,612 '15':293 '17':920,1081,1103 '2':845 '20':436 '3':878 '4':891 '8':723 '9':663 'access':218,1417 'across':229,792,914,1246,1298 'act':1116 'ad':1129 'add':963 'adopt':16,52 'alway':212,765 'animatingdiffer':528 'app':12,48,794,1077,1451 'appl':1468 'appli':1047 'ascend':433 'async':259,289,305,624,675,727 'attribut':101,104,1101,1106,1111,1133,1138,1147 'auto':333 'auto-merg':332 'automat':609 'automaticallymergeschangesfrompar':242,1418 'avail':566 'avoid':294,1249,1347 'await':265,311,372,630,681,733 'back':768 'background':200,251,1404 'batch':58,83,86,586,588,773,775,1313,1367,1373,1430 'batch-oper':85 'batchimport':620 'beneath':1139 'bgcontext':369 'bgcontext.existingobject':377 'bgcontext.perform':373 'bgcontext.save':385 'bound':190 'boundari':233,350,1466 'build':4,39 'built':399 'built-in':398 'bulk':34 'bundle.main':974,980 'bundle.main.url':1267 'bypass':595,1375 'cach':405,1358 'cachenam':446,556,1365 'call':542,583,1316,1320,1359 'callback':578 'cditem':319 'cdtrip':275,381,1214 'cdtrip.entity':636,739 'cdtrip.fetchrequest':428,686,1227 'cdtrip.startdate':432 'cell':493,508 'cell.textlabel':504 'cellforrowat':488 'chang':401,546,577,767,791,834 'check':1323 'checklist':113,116,1391 'checksum':944,1092 'checksumv1':954,960 'checksumv2':956,961 'class':136,416 'cloudkit':178 'codabl':1150 'coexist':1154 'common':107,110,1292 'common-mistak':109 'communic':351 'compil':947 'complet':330 'composit':100,103,1100,1105,1131,1146 'composite-attribut':102 'concurr':57,74,78,183,284 'concurrency-and-thread':77 'concurrencytyp':1332 'configur':336,1287 'confin':1333 'conflict':1348,1428 'contain':145,149,992,1175,1200,1206,1281 'container.loadpersistentstores':153,1066,1189 'container.newbackgroundcontext':176,858,998 'container.persistentstorecoordinator':844 'container.persistentstoredescriptions':823,1064,1184 'container.viewcontext':172,1211 'container.viewcontext.automaticallymergeschangesfromparent':165 'container.viewcontext.mergepolicy':167 'content':68 'contentsof':1274 'context':188,201,220,230,252,262,308,320,321,581,599,607,627,678,730,771,857,997,1210,1215,1216,1309,1405,1416,1436 'context.execute':644,704,748,869,901 'context.existingobject':271 'context.fetch':1014,1231 'context.haschanges':1327 'context.perform':266,312,631,682,734,859 'context.performandwait':1000 'context.save':282,327,1046,1223 'control':438,451,513,514 'controller.delegate':448 'core':2,8,37,45,124,160,186,393,1470 'core-data':1 'coredata':134,413,938,1165 'coredatastack':137,143 'coredatastack.shared.newbackgroundcontext':263,309,370,628,679,731 'coredatastack.shared.viewcontext':443,660,720,764 'coredatatest':1169 'correct':913 'count':468 'cover':54 'crash':1349,1429 'createandfetchtrip':1203 'cross':349,1464 'cross-boundari':348 'currentstag':988 'custom':930 'cutoff':673,691 'data':3,9,38,42,46,125,161,187,394,1002,1471 'datasource.apply':526 'date':674 'defin':939,1122 'deleg':562 'delet':776,782,1357,1368,1371,1374,1376 'deletecach':543,1360 'deleteoldtrip':671 'deni':781,1370 'deprec':1330 'descript':809,824,1058,1065,1180,1185 'description.setoption':811,817,1060 'description.type':1182 'descriptor':541,553,1442 'didchangecontentwith':516,564 'diffabl':511,560 'doc':127,206,406,613,664,799,931,1141,1469 'drive':388 'driven':28 'duplic':1250 'editor':1127 'effici':387 'els':276,994 'enabl':804,1446 'encapsul':122 'enddat':690 'enforc':779 'entiti':635,738,1114,1251 'entitynam':1009 'error':154,158,164,1067,1071,1075,1188,1190,1193 'execut':590 'expect':1233,1236 'extens':795 'fail':163,1074 'fals':434,742 'fast':602 'fatalerror':159,1072 'fetch':240,361,395,533,548,826,846,1305,1351,1438 'fetchedresultscontrol':423 'fetchedresultscontroller.object':501 'fetchedresultscontroller.performfetch':457 'fetchedresultscontroller.sections':467,479 'fetchrequest':440,684,697,698 'fetchrequest.predicate':687 'final':135 'first':1328,1362 'firstnam':1037 'firstname/lastname':985 'fix':1295 'forget':1310 'forkey':815,821,1023,1036,1043,1062 'format':689 'forresourc':1268 'fromcontextdidsav':887 'fromremotecontextsav':656,716,760,1318 'fullnam':1021 'fullname.split':1029 'func':173,253,301,453,461,471,484,512,619,670,725,852,1170,1202,1276 'group':1107 'guard':267,990,1325 'haschang':1324 'histori':62,89,93,785,805,847,894,910,1444 'human':1096 'human-read':1095 'id':255,273,650,658,710,718,754,762 'ignor':1369 'import':133,412,414,937,1164,1166 'importitem':302 'improv':7 'in-memori':1157,1288,1454 'indexpath':489,490,498,503 'init':148,1331 'initi':1394 'instanc':228,1245,1463 'instead':182,236,1389 'int':466,477,478 'integr':285 'io':292,567,611,662,722,919,1080,1102 'isfavorit':741,964 'item':318 'item.id':322 'item.title':324 'itemrecord':304 'key':530 'keypath':431 'last':849 'lastnam':1044 'lasttoken':904 'launch':915 'lazi':421 'least':538 'let':141,144,157,261,268,307,317,363,368,374,425,437,492,499,519,626,632,641,649,677,683,694,701,709,729,735,745,753,808,856,860,866,873,895,953,955,957,966,991,996,1006,1011,1020,1027,1052,1057,1070,1174,1179,1195,1205,1209,1212,1224,1228,1262,1265,1280 'level':594,790 'lightweight':928,1083 'list':27 'list-driven':26 'live':1354 'load':1258 'loaderror':1187,1192,1196,1198 'logic':1120 'main':198,1402 'mainqueueconcurrencytyp':1338 'maintain':41 'maketestcontain':1171,1208,1277 'manag':343,597,1053,1061 'managedobjectcontext':442 'managedobjectmodel':1285 'manual':1379 'map':1087,1148 'mark':295,458,509,1380 'markalltripsasnotfavorit':726 'maxsplit':1031 'memori':1159,1290,1456 'merg':334,766,827,879,1312,1433 'mergechang':1317 'mergepolici':1341,1422 'method':563 'migrat':65,96,99,917,923,940,1073,1084 'migratingfrom':969 'migrationmanag':987 'migrationmanager.container':993 'mistak':108,111,1293,1294 'model':948,951,1088,1098,1126,1257 'modelv2':972 'modelv3':978 'modifi':1350 'momd':1271 'multi':1449 'multi-target':1448 'must':535 'myappmodel':152,1178,1269,1284 'name':151,841,952,971,977,983,1024,1099,1177,1238,1283 'need':347 'nest':1135 'never':225 'newbackgroundcontext':174 'newnam':257,280 'nil':445,558,1366 'notif':608,835,854,855 'notificationcenter.default.addobserver':836 'nsbatchdeleterequest':31,661,665,696 'nsbatchdeleteresult':707 'nsbatchinsertrequest':30,610,614,634 'nsbatchinsertresult':647 'nsbatchupdaterequest':32,721,737 'nsbatchupdateresult':751 'nscompositeattributedescript':1142 'nscustommigrationstag':968 'nsdate':693 'nsdeletedobjectskey':717 'nsdiffabledatasourcesnapshot':523 'nsdiffabledatasourcesnapshotrefer':518 'nsfetchedresultscontrol':24,60,81,82,386,407,424,439,515,1355,1437,1479 'nsfetchedresultscontrollerdeleg':419,510 'nsfetchrequest':427,685,1008,1226 'nsinfermappingmodelautomaticallyopt':1085 'nsinmemorystoretyp':1183 'nsinsertedobjectskey':657 'nslightweightmigrationstag':959,1089 'nsmanagedobject':22,227,296,1297,1381,1462 'nsmanagedobjectcontext':171,175,207 'nsmanagedobjectcontext.mergechanges':655,715,759 'nsmanagedobjectcontext.perform':286 'nsmanagedobjectid':235,256,525,654,714,758 'nsmanagedobjectmodel':1244,1264,1273,1460 'nsmanagedobjectmodelrefer':970,976 'nsmergebypropertyobjecttrumpmergepolici':168,1345 'nsnumber':814,820 'nsper':36 'nspersistentcloudkitcontain':181 'nspersistentcontain':121,128,146,150,1173,1176,1279,1282,1392,1475 'nspersistenthistorychangerequest':800 'nspersistenthistorychangerequest.deletehistory':897 'nspersistenthistorychangerequest.fetchhistory':862 'nspersistenthistoryresult':872 'nspersistenthistorytrackingkey':816 'nspersistenthistorytransact':877 'nspersistentstoredescript':810,1059,1181 'nspersistentstoreremotechang':842 'nspersistentstoreremotechangenotificationpostoptionkey':822 'nspersistentstorestagedmigrationmanageroptionkey':1063 'nspredic':688 'nssortdescriptor':430 'nsstagedmigrationmanag':918,932,1054,1483 'nsupdatedobjectskey':761 'numberofobject':481 'numberofrowsinsect':475 'numberofsect':462 'objc':851 'object':344,598,637,843 'objectid':354,364,379,640,1301,1388 'observ':825,832 'off-queu':1413 'old':893 'older':574 'one':539 'oper':35,59,84,87,202,587,589,774,1314,1431 'option':404 'order':925 'overload':291 'overrid':452,460,470,483 'part':1028 'parts.count':1039 'parts.first':1035 'parts.last':1042 'pass':226,234,352,1296,1300,1387 'peopl':1012,1019 'per':576,907 'per-chang':575 'perform':214,1408 'performandwait':216,1410 'performfetch':584 'persist':10,43,61,88,92,784,1050,1443 'persistenceerror.notfound':278 'persistent-history-track':91 'person':1010,1017 'person.setvalue':1033,1038 'person.value':1022 'point':531 'predic':550 'prefer':571 'prevent':1427 'privat':147,204,420,1261 'privatequeueconcurrencytyp':1336 'process':798,912 'properti':965 'purg':829,892 'purgerequest':896,902 'queue':192,199,205,224,1403,1415 're':239,360,1304 're-fetch':238,359,1303 'readabl':1097 'record':303,314,316,621,638 'record.id':323 'record.title':325 'refer':117,118,1467 'relev':770,1435 'remot':833 'request':396,426,441,534,549,633,645,695,705,736,749,861,870,1007,1015,1225,1232,1352,1439 'request.fetchbatchsize':435 'request.propertiestoupdate':740 'request.resulttype':639,699,743 'request.sortdescriptors':429 'reset':582 'result':642,651,652,702,711,712,746,755,756,867,1315,1432 'result.result':875 'resulttypeobjectid':700 'return':450,507,995,1199,1272 'review':5,112,115,1390 'review-checklist':114 'rule':211,783,1372,1377 'save':329,1321 'scenario':1155 'schema':922,1004 'section':476,480 'sectionnamekeypath':444 'selector':838,839 'self':449,837 'self.container.viewcontext.mergechanges':886 'self.lasttoken':864,889,899 'sendabl':139,299,341,357,366,1384 'separ':1030 'sequenc':921 'set':241,555,1340,1344,1419,1423 'setup':56,70,73,120 'share':142,1242,1256,1397,1459 'sharedmodel':1263,1286 'sinc':848 'singl':1119,1255 'skill' 'skill-core-data' 'snapshot':517,520,521,527,561 'sort':540,552,1441 'sosumi.ai':130,209,409,616,667,802,934,1144,1473,1477,1481,1485 'sosumi.ai/documentation/coredata)':1472 'sosumi.ai/documentation/coredata/nsbatchdeleterequest)':666 'sosumi.ai/documentation/coredata/nsbatchinsertrequest)':615 'sosumi.ai/documentation/coredata/nscompositeattributedescription)':1143 'sosumi.ai/documentation/coredata/nsfetchedresultscontroller)':408,1480 'sosumi.ai/documentation/coredata/nsmanagedobjectcontext)':208 'sosumi.ai/documentation/coredata/nspersistentcontainer)':129,1476 'sosumi.ai/documentation/coredata/nspersistenthistorychangerequest)':801 'sosumi.ai/documentation/coredata/nsstagedmigrationmanager)':933,1484 'source-dpearson2699' 'split':982 'sql':593 'stack':55,69,72,119,126 'stack-setup':71 'stage':64,95,98,916,926,941 'stage1to2':958,1055 'stage1to2.label':962 'stage2to3':967,1056 'stage2to3.label':981 'stage2to3.willmigratehandler':986 'staged-migr':97 'static':140 'store':162,789,903,1051,1160,1291,1457 'store-level':788 'storeremotechang':840,853 'string':258,524,622,1026,1034,1041,1093 'struct':1151,1168 'sub':1110,1137 'sub-attribut':1109,1136 'subclass':23,297 'super.viewdidload':455 'support':1104 'swift':132,247,283,300,362,411,618,669,724,807,830,936,1163,1260 'swiftdata':17,53,1153 'sync':179 'tableview':464,472,473,485,486 'tableview.dequeuereusablecell':494 'take':1090 'target':793,908,1078,1308,1450 'task.detached':367 'test':67,105,106,1156,1162,1167,1201,1218,1239,1247,1452 'text':505 'thread':76,80,185,232,1299,1465 'throw':260,277,290,306,625,676,728,1172,1197,1204,1278 'tip':1241 'token':850 'topic-accessibility' 'topic-agent-skills' 'topic-ai-coding' 'topic-apple' 'topic-claude-code' 'topic-codex-skills' 'topic-cursor-skills' 'topic-ios' 'topic-ios-development' 'topic-liquid-glass' 'topic-localization' 'topic-mapkit' 'track':63,90,94,402,786,787,806,1445 'transact':874,883,885 'transaction.objectidnotification':888 'transaction.token':890 'transform':1001 'tri':264,270,281,310,326,371,376,384,456,629,643,680,703,732,747,868,900,999,1013,1045,1207,1222,1230 'trigger':606 'trip':269,375,500,1213,1219,1229,1240 'trip.isfavorite':382 'trip.name':279,506,1217 'trip.objectid':365 'trip.startdate':1220 'tripcel':496 'trips.count':1234 'trips.first':1237 'tripscach':447 'tripsviewcontrol':417 'true':166,243,383,529,812,818 'type':1132,1334 'ui':29 'uicollectionview':390 'uikit':415 'uitableview':389,465,474,487 'uitableviewcel':491 'uitableviewcontrol':418 'uitableviewdatasourc':459 'uncheck':138,340,1383 'unit':1121 'updatedobjectidsresulttyp':744 'updatetrip':254 'url':1266,1275 'use':18,44,180,213,339,942,1082,1253,1329,1335,1364,1399,1453 'userdefault':906 'valid':1378 'var':169,422,1186 'version':943,949,1005,1091 'viewcontext':170,194,246,331,881,1343,1398,1421,1425 'viewdidload':454 'warn':1252 'widget':796 'withextens':1270 'withidentifi':495 'withnam':544,1361 'without':1322,1356 'work':20 'wrap':1411 'write':248,1407","prices":[{"id":"ac909b79-be1f-436b-8af5-f2ffbbb80b4c","listingId":"015bc6f6-b1b4-432d-97a8-845773d57971","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"dpearson2699","category":"swift-ios-skills","install_from":"skills.sh"},"createdAt":"2026-04-23T00:53:22.187Z"}],"sources":[{"listingId":"015bc6f6-b1b4-432d-97a8-845773d57971","source":"github","sourceId":"dpearson2699/swift-ios-skills/core-data","sourceUrl":"https://github.com/dpearson2699/swift-ios-skills/tree/main/skills/core-data","isPrimary":false,"firstSeenAt":"2026-04-23T00:53:22.187Z","lastSeenAt":"2026-05-18T18:53:40.610Z"},{"listingId":"015bc6f6-b1b4-432d-97a8-845773d57971","source":"skills_sh","sourceId":"dpearson2699/swift-ios-skills/core-data","sourceUrl":"https://skills.sh/dpearson2699/swift-ios-skills/core-data","isPrimary":true,"firstSeenAt":"2026-05-07T20:41:59.684Z","lastSeenAt":"2026-05-07T22:41:20.094Z"}],"details":{"listingId":"015bc6f6-b1b4-432d-97a8-845773d57971","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"dpearson2699","slug":"core-data","github":{"repo":"dpearson2699/swift-ios-skills","stars":599,"topics":["accessibility","agent-skills","ai-coding","apple","claude-code","codex-skills","cursor-skills","ios","ios-development","liquid-glass","localization","mapkit","networking","storekit","swift","swift-concurrency","swiftdata","swiftui","widgetkit","xcode"],"license":"other","html_url":"https://github.com/dpearson2699/swift-ios-skills","pushed_at":"2026-04-26T21:04:17Z","description":"Agent Skills for iOS 26+, Swift 6.3, SwiftUI, and modern Apple frameworks","skill_md_sha":"3bdf48b9142120ef6645c1dc8993078c9dc4a65f","skill_md_path":"skills/core-data/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/dpearson2699/swift-ios-skills/tree/main/skills/core-data"},"layout":"multi","source":"github","category":"swift-ios-skills","frontmatter":{"name":"core-data","description":"Build, review, or improve Core Data persistence in apps that have not adopted SwiftData. Use when working with NSManagedObject subclasses, NSFetchedResultsController for list-driven UI, NSBatchInsertRequest / NSBatchDeleteRequest / NSBatchUpdateRequest for bulk operations, NSPersistentHistoryChangeRequest for persistent history tracking and multi-target sync, NSStagedMigrationManager for staged schema migrations (iOS 17+), NSCompositeAttributeDescription for composite attributes (iOS 17+), or when integrating Core Data threading with Swift Concurrency. For Core Data + SwiftData coexistence or migration, see the swiftdata skill instead."},"skills_sh_url":"https://skills.sh/dpearson2699/swift-ios-skills/core-data"},"updatedAt":"2026-05-18T18:53:40.610Z"}}