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
data-ai
license: Apache-2.0 NOT for unrelated tasks outside this domain.
development
Use when designing caching strategies (cache-aside, write-through, write-behind), implementing distributed locks, building rate limiters, leaderboards, real-time streams (XADD/consumer groups), pub/sub, or tuning eviction policies. Triggers: thundering-herd on cache miss, dogpile on key expiry, Redlock vs SET-NX-PX choice, sliding-window rate limiter, hot-key on a single cluster slot, big-key blowup, MULTI/EXEC across slots, KEYS in production. NOT for Redis Cluster operations/admin (different domain), embedded KV (SQLite, leveldb), in-process LRU caches, or Memcached.
tools
Drawing the `'use client'` boundary correctly in React Server Components apps (Next.js App Router, RSC frameworks) — leaf-pushing, slot composition, serialization rules, and environment poisoning prevention. Grounded in react.dev and Next.js 16 docs.
development
Use when designing rate limiting for an API, choosing between token bucket / sliding window / leaky bucket / fixed window, implementing it in Redis, deciding edge (Cloudflare/Upstash) vs origin enforcement, sizing per-user vs per-IP vs per-endpoint quotas, returning the right 429 response with Retry-After, or fixing the boundary-burst bug in fixed-window limiters. Triggers: 429 too many requests, INCR + EXPIRE, ZADD + ZREMRANGEBYSCORE + ZCARD, X-RateLimit-Remaining header, Cloudflare WAF rate limiting rules, Upstash @upstash/ratelimit, leaky bucket shaping vs policing, distributed rate limiter consistency. NOT for DDoS mitigation specifically (different scale), CAPTCHA / bot management, full WAF design, or per-user quota billing.