Core Data Agent Skill

repository·main·Indexed 43 days ago

https://github.com/avdlee/core-data-agent-skill

Expert-level guidance for AI coding agents to assist with safe persistence, performance optimization, and schema migration in Core Data projects. It provides actionable references on thread safety, CloudKit integration, batch operations (NSBatchInsertRequest, NSBatchDeleteRequest, NSBatchUpdateRequest), and testing patterns.

Tokens
43.8K
Snippets
92
Records
139
Agent score
55%

What's inside Core Data Agent Skill

  1. Navigate Core Data topics via the Reference Index

    main

    The Reference Index provides a categorized map of all available Core Data expertise within this skill. Use it to locate specific guides based on your current task or problem.

    Topic Categories

    • Fundamentals: Stack setup (NSPersistentContainer), saving strategies, and project auditing.
    • Data Access: Fetch requests, threading models, Swift Concurrency (async/await), and batch operations.
    • Model & Schema: Model configuration (constraints, validation) and migration strategies (lightweight, staged, deferred).
    • Advanced Topics: Persistent history tracking, CloudKit integration, performance profiling, and testing patterns.

    Problem-Based Navigation

    If you are facing a specific issue, you can jump directly to relevant documentation:

    • Errors:
      • NSPersistentStoreIncompatibleVersionHashError $\rightarrow$ migration.md
      • NSMergeConflict $\rightarrow$ stack-setup.md or model-configuration.md
      • Cannot delete objects in other contexts $\rightarrow$ threading.md
    • Tasks:
      • Import large datasets $\rightarrow$ batch-operations.md
      • Sync with CloudKit $\rightarrow$ cloudkit-integration.md
      • Write tests $\rightarrow$ testing.md
      • Optimize queries $\rightarrow$ fetch-requests.md or performance.md
  2. Core Data Agent Skill Capabilities Overview

    main

    The core-data-expert skill provides comprehensive guidance for Core Data development, covering the following domains:

    • Persistence Decisions: Choosing between NSPersistentContainer and NSPersistentCloudKitContainer, managing view vs. background contexts, and handling merge policies.
    • Thread Safety: Avoiding pitfalls with NSManagedObjectID, preventing data races, and using perform vs performAndWait correctly.
    • Performance Optimization: Writing efficient fetch requests (batching, property limiting), managing memory (context resets, faulting), and using batch operations.
    • Schema Migration: Implementing lightweight, staged (iOS 17+), and deferred (iOS 14+) migrations.
    • CloudKit Integration: Setting up NSPersistentCloudKitContainer, designing CloudKit-compatible schemas, and debugging sync issues.
    • Testing: Using in-memory stores and data generators for Core Data testing.
  3. Avoid entity description conflicts with the Shared Model Pattern

    main

    Creating multiple NSManagedObjectModel instances can lead to the error: Failed to find a unique match for an NSEntityDescription.

    To prevent this, implement a shared model instance via an extension on NSManagedObjectModel. This ensures all test containers use the exact same model definition.

    extension NSManagedObjectModel {
        static let shared: NSManagedObjectModel = {
            guard let modelURL = Bundle.main.url(forResource: "Model", withExtension: "momd"),
                  let model = NSManagedObjectModel(contentsOf: modelURL) else {
                fatalError("Failed to load model")
            }
            return model
        }()
    }
    
    // Use in tests
    container = NSPersistentContainer(name: "Model", managedObjectModel: .shared)
  4. Control faulting and prefetching for performance

    main

    Core Data uses 'faulting' to keep memory usage low, but accessing properties on a fault can trigger individual database hits (the N+1 problem). Use these strategies to optimize performance:

    Prefetching Relationships

    Use relationshipKeyPathsForPrefetching to fetch related objects in a single batch. This is highly recommended when you know you will access relationships in a loop.

    Returning Objects without Faults

    Set returnsObjectsAsFaults = false if you know you will access all properties of the fetched objects immediately. This is useful for small result sets but should be avoided for very large datasets to prevent high memory usage.

    // Prefetching
    let fetchRequest = Article.fetchRequest()
    fetchRequest.relationshipKeyPathsForPrefetching = ["category", "attachments"]
    
    // Disabling faults
    fetchRequest.returnsObjectsAsFaults = false
  5. How Persistent History Tracking components work together

    main

    A complete persistent history implementation typically consists of four functional components:

    1. Observer: Listens for .NSPersistentStoreRemoteChange notifications from the NSPersistentStoreCoordinator.
    2. Fetcher: Uses NSPersistentHistoryChangeRequest to retrieve transactions from the log, often using an NSPersistentHistoryToken to fetch only changes occurring after the last processed transaction.
    3. Merger: Takes the fetched transactions and applies them to the active NSManagedObjectContext (usually the viewContext) using NSManagedObjectContext.mergeChanges(fromRemoteContextSave:into:).
    4. Cleaner: Periodically executes NSPersistentHistoryChangeRequest.deleteHistory(before:) to remove old transactions and prevent the log from growing indefinitely.
  6. Understand Core Data Stack Components

    main

    The Core Data stack consists of several key components that manage the lifecycle of your data:

    • NSPersistentContainer: The primary entry point that encapsulates the entire stack (model, coordinator, and contexts).
    • NSPersistentCloudKitContainer: A subclass of NSPersistentContainer that adds CloudKit synchronization capabilities.
    • NSPersistentStoreCoordinator: The central hub that manages one or more persistent stores (like SQLite) and coordinates access between them and the contexts.
    • NSManagedObjectContext (MOC): A 'scratch pad' used to work with objects. Changes made here are only in-memory and are not persisted to the store until you call save().
    • NSManagedObject (MO): The base class for all data objects. Each instance represents a single record (row) in your data model.
    • NSManagedObjectID: A unique, immutable, and thread-safe identifier for a managed object.
  7. Saving in different contexts (View, Background, and Nested)

    main

    Core Data supports different concurrency models for saving:

    • View Context: Used for the main thread; typically used for UI-related data.
    • Background Context: Created via container.newBackgroundContext(). Use context.perform { ... } to ensure thread safety when making changes and saving.
    • Nested Contexts: A child context can have a parent context. Saving a child context pushes changes to the parent but does not persist them to disk. You must save the parent context to commit changes to the persistent store.
    // Nested Context Example
    let parentContext = container.viewContext
    let childContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
    childContext.parent = parentContext
    
    // 1. Save child to push changes to parent
    try? childContext.save()
    
    // 2. Save parent to persist to disk
    try? parentContext.save()
  8. Choose the correct NSManagedObjectContext type

    main

    Core Data uses different context types to manage threading and data flow:

    • View Context: The main queue context. Use this exclusively for UI operations and anything running on the main thread.
    • Background Context: A private queue context. Use this for heavy processing, data imports, or complex calculations to avoid blocking the UI. It runs on a background thread.
    • Child Context: A context that has a parent context. When a child context saves, it pushes its changes to the parent context rather than directly to the persistent store.
  9. Core Data Thread Safety Principles

    main

    When using Core Data with Swift Concurrency, the fundamental thread safety rules remain unchanged:

    • Do not pass NSManagedObject between threads/tasks. Managed objects are not Sendable because they are mutable and tied to a specific context.
    • Access objects on their context's thread. Always ensure you are interacting with an object within the isolation domain of its parent NSManagedObjectContext.
    • Use NSManagedObjectID for cross-thread communication. NSManagedObjectID is thread-safe and can be passed between tasks or contexts to re-fetch the object in the target context.

    Warning: Avoid using @unchecked Sendable to bypass compiler warnings; this hides safety issues without actually fixing the underlying thread-safety violations.

  10. Implement type-safe fetch requests with a Managed protocol

    main

    To avoid manual string-based entity names and improve type safety, you can implement a Managed protocol. This allows you to call EntityName.fetchRequest() and receive a typed NSFetchRequest<EntityName>.

    1. Define a Managed protocol conforming to NSManagedObject.
    2. Provide a default implementation for entityName and a generic fetchRequest<T>() method.
    3. Extend your Core Data entities to conform to Managed.

    This pattern ensures that your fetch requests are always correctly typed to the entity they are querying.

    protocol Managed: NSManagedObject {
        static var entityName: String { get }
    }
    
    extension Managed {
        static var entityName: String {
            return String(describing: self)
        }
        
        static func fetchRequest<T: NSManagedObject>() -> NSFetchRequest<T> {
            return NSFetchRequest<T>(entityName: entityName)
        }
    }
    
    // Conform your entities
    extension Article: Managed {}
    
    // Usage
    let fetchRequest: NSFetchRequest<Article> = Article.fetchRequest()
  11. Supported Lightweight Migration Operations

    main

    Lightweight migration automatically handles the following changes:

    Attributes:

    • Add or remove an attribute.
    • Change an attribute from optional to non-optional (requires a default value).
    • Change an attribute from non-optional to optional.
    • Rename an attribute (requires a Renaming Identifier).

    Relationships:

    • Add or remove a relationship.
    • Rename a relationship (requires a Renaming Identifier).
    • Change cardinality (e.g., to-one to to-many).
    • Change ordering (ordered vs non-ordered).

    Entities:

    • Add or remove an entity.
    • Rename an entity (requires a Renaming Identifier).
    • Create parent/child entity relationships.
    • Move attributes or entities within the hierarchy.

    Limitations:

    • You cannot merge entity hierarchies (entities without a common parent cannot be merged into a shared parent via lightweight migration).
  12. Core Data Expert Agent Behavior Contract

    main

    When using the core-data-expert skill, the agent follows a specific set of rules to ensure production-grade guidance. Key principles include:

    • Context Awareness: Always distinguishes between the view context (UI) and background context (heavy work).
    • Thread Safety: Recommends using NSManagedObjectID for cross-context or cross-task communication. Never pass NSManagedObject instances across contexts.
    • Migration Strategy: Prefers lightweight migration, but suggests staged migration (available in iOS 17+) for complex schema changes.
    • Batch Operations: When recommending batch operations, the agent verifies that persistent history tracking is enabled to ensure UI updates.
    • CloudKit Constraints: Reminds developers that the Production schema is immutable in CloudKit.
    • Deployment Targets: Advice is tailored based on the OS/deployment target (e.g., iOS 14+ vs iOS 17+ features).