skills/ios-core-data-architect/SKILL.md
iOS Core Data architect for persistent storage, CloudKit sync, schema migrations, and SwiftData migration. Activate on: Core Data, NSManagedObject, CloudKit sync, Core Data migration, NSFetchedResultsController, NSPersistentContainer, SwiftData migration path. NOT for: SwiftUI state management (use swiftui-data-flow-expert), server databases (use data-pipeline-engineer), SQLite direct (use mobile-offline-sync-architect).
npx skillsauth add curiositech/windags-skills ios-core-data-architectInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Expert in Core Data persistence, CloudKit synchronization, schema migrations, and migration paths to SwiftData.
Activate on: "Core Data", "NSManagedObject", "CloudKit sync", "Core Data migration", "NSFetchedResultsController", "NSPersistentContainer", "SwiftData migration", "lightweight migration", "Core Data performance"
NOT for: SwiftUI state management → swiftui-data-flow-expert | Server databases → data-pipeline-engineer | SQLite direct → mobile-offline-sync-architect
| Domain | Technologies | |--------|-------------| | Persistence | NSPersistentContainer, NSManagedObjectContext, WAL mode | | CloudKit | NSPersistentCloudKitContainer, CKRecord zone, conflict resolution | | Migrations | Lightweight migration, mapping models, progressive migration | | Performance | NSBatchInsertRequest, NSBatchDeleteRequest, faulting, prefetch | | SwiftData | Coexistence with Core Data, migration path, @Model from NSManagedObject |
class PersistenceController {
static let shared = PersistenceController()
let container: NSPersistentCloudKitContainer
init(inMemory: Bool = false) {
container = NSPersistentCloudKitContainer(name: "MyApp")
guard let description = container.persistentStoreDescriptions.first else {
fatalError("No store description")
}
if inMemory {
description.url = URL(fileURLWithPath: "/dev/null")
}
// CloudKit configuration
description.cloudKitContainerOptions = NSPersistentCloudKitContainerOptions(
containerIdentifier: "iCloud.com.example.myapp"
)
// Enable remote change notifications
description.setOption(true as NSNumber,
forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
// Enable persistent history tracking
description.setOption(true as NSNumber,
forKey: NSPersistentHistoryTrackingKey)
container.loadPersistentStores { _, error in
if let error { fatalError("Store failed: \(error)") }
}
container.viewContext.automaticallyMergesChangesFromParent = true
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
}
// Background context for heavy operations
func newBackgroundContext() -> NSManagedObjectContext {
let context = container.newBackgroundContext()
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
return context
}
}
Version 1 → Version 2 (add optional column):
└─ Lightweight migration (automatic)
Version 2 → Version 3 (rename attribute):
└─ Mapping model required (heavyweight)
Version 3 → Version 4 (split entity):
└─ Custom migration with NSEntityMigrationPolicy
Strategy: Progressive migration chain
v1 → v2 → v3 → v4 (each step is a known migration)
NOT: v1 → v4 directly (complex, error-prone)
Code:
for migration in migrationChain {
try coordinator.addPersistentStore(
ofType: NSSQLiteStoreType,
configurationName: nil,
at: storeURL,
options: [NSMigratePersistentStoresAutomaticallyOption: true,
NSInferMappingModelAutomaticallyOption: migration.isLightweight]
)
}
// Phase 1: Core Data and SwiftData side-by-side
// Share the same SQLite store file
let schema = Schema([NewEntity.self]) // SwiftData models
let config = ModelConfiguration(
url: existingCoreDataStoreURL // Same store as Core Data
)
let container = try ModelContainer(for: schema, configurations: [config])
// Phase 2: Gradually move entities from Core Data to SwiftData
// - New entities: @Model (SwiftData)
// - Existing entities: NSManagedObject (Core Data)
// - Read from both, write to SwiftData for new data
// Phase 3: Full migration
// - Convert all NSManagedObject subclasses to @Model
// - Remove .xcdatamodeld file
// - Use ModelContainer exclusively
performBackgroundTask or newBackgroundContext() for inserts and batch operations.NSBatchInsertRequest which bypasses the context and writes directly.fetchBatchSize.NSPersistentHistoryTrackingKey.[ ] Persistent container configured correctly (CloudKit or local)
[ ] viewContext used only for reads; background context for writes
[ ] NSBatchInsertRequest used for bulk operations
[ ] Fetch requests have fetchBatchSize set (typically 20-50)
[ ] Migration plan documented for each model version
[ ] Lightweight migration tested between all adjacent versions
[ ] CloudKit sync tested (if applicable) with conflict resolution
[ ] Persistent history tracking enabled
[ ] NSFetchedResultsController used for table/list data sources
[ ] Background context mergePolicy set explicitly
[ ] Unit tests use in-memory store for speed
[ ] SwiftData migration path documented for future transition
tools
Building resilient distributed systems with circuit breakers, retries with full-jitter exponential backoff, retry budgets (per-request 3-attempt + per-client 10% ratio per Google SRE), deadline propagation, and the cascading-failure math (4 layers × 3 retries = 64x amplification). Grounded in Resilience4j, Microsoft Cloud Patterns, AWS Architecture Blog (Marc Brooker), and Google SRE Book.
testing
Designing HTTP cache headers that work correctly across browsers, CDNs, and shared proxies — `Cache-Control` directives per RFC 9111, `stale-while-revalidate` and `stale-if-error` per RFC 5861, the Vary header for varying responses, and surrogate keys for tag-based purging. Grounded in IETF RFCs and Cloudflare/Fastly docs.
development
Use when designing or fixing a Content Security Policy on a real site, choosing between nonce-based and hash-based CSP, adding strict-dynamic, debugging "Refused to execute inline script" errors, deploying CSP in report-only mode first, configuring report-to / report-uri, or auditing an existing policy for unsafe-inline / unsafe-eval / wildcards. Triggers: "CSP blocks legitimate inline script", strict-dynamic, nonce-{RANDOM}, sha256-{HASH}, object-src none, base-uri none, frame-ancestors, Trusted Types, X-Content-Security-Policy obsolete, report-only vs enforced. NOT for general HTTP security headers (HSTS, COOP/COEP), Trusted Types deep dive, CORS configuration, or building a WAF.
tools
Choosing and operating an HTTP API versioning strategy that doesn't break clients — Stripe's date-based pinned versions, the Deprecation/Sunset header pair (RFC 9745 + RFC 8594), URI vs header vs media-type approaches, and the version-transformer pattern. Grounded in Stripe's published architecture and IETF RFCs.