### Install Core Data Agent Skill with skills.sh Source: https://github.com/avdlee/core-data-agent-skill/blob/main/README.md Use this command to install the Core Data Agent Skill using the skills.sh tool. This is the recommended installation method. ```bash npx skills add https://github.com/avdlee/core-data-agent-skill --skill core-data-expert ``` -------------------------------- ### Basic CloudKit Setup and Sync Event Monitoring Source: https://context7.com/avdlee/core-data-agent-skill/llms.txt Demonstrates the basic setup for NSPersistentCloudKitContainer and how to observe CloudKit sync events. Ensure your schema adheres to CloudKit constraints. ```swift import CoreData, CloudKit // Basic setup (replace NSPersistentContainer → NSPersistentCloudKitContainer) let container = NSPersistentCloudKitContainer(name: "Model") container.loadPersistentStores { _, error in if let error { fatalError("Store load failed: \(error)") } } // Monitor sync events NotificationCenter.default.addObserver( forName: NSPersistentCloudKitContainer.eventChangedNotification, object: container, queue: .main ) { notification in guard let event = notification.userInfo?[ NSPersistentCloudKitContainer.eventNotificationUserInfoKey ] as? NSPersistentCloudKitContainer.Event else { return } switch event.type { case .setup: print("Setup \(event.succeeded ? "OK" : "FAILED")") case .import: print("Import \(event.succeeded ? "OK" : "FAILED")") case .export: print("Export \(event.succeeded ? "OK" : "FAILED")") @unknown default: break } if let error = event.error { print("Sync error: \(error)") } } ``` -------------------------------- ### Install Core Data Agent Skill via skills.sh Source: https://context7.com/avdlee/core-data-agent-skill/llms.txt Use this command to install the skill for use with an AI agent. After installation, you can instruct the agent to use the 'core-data-expert' skill. ```bash npx skills add https://github.com/avdlee/core-data-agent-skill --skill core-data-expert # Then in your AI agent: # "Use the core data skill and analyze the current project for Core Data improvements" ``` -------------------------------- ### Complete Core Data Object Lifecycle Example Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/model-configuration.md A comprehensive example demonstrating the implementation of multiple lifecycle methods within an `NSManagedObject` subclass to manage object creation, saving, and deletion. ```swift class Article: NSManagedObject { @NSManaged var name: String? @NSManaged var creationDate: Date? @NSManaged var lastModified: Date? @NSManaged var localResourceURL: URL? override func awakeFromInsert() { super.awakeFromInsert() // Set creation date once setPrimitiveValue(Date(), forKey: #keyPath(Article.creationDate)) setPrimitiveValue(Date(), forKey: #keyPath(Article.lastModified)) } override func willSave() { super.willSave() // Update modification date on every save if !isDeleted && changedValues().keys.contains("name") { setPrimitiveValue(Date(), forKey: #keyPath(Article.lastModified)) } // Clean up files when deleted if isDeleted, let url = localResourceURL { try? FileManager.default.removeItem(at: url) } } override func prepareForDeletion() { super.prepareForDeletion() // Cancel ongoing operations // Don't delete files here! } } ``` -------------------------------- ### Production-Ready Core Data Stack Setup Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/stack-setup.md This class provides a singleton instance for managing the Core Data stack, including persistent container setup, persistent history tracking, and background context creation. It's designed for production use with appropriate error handling and merge policies. ```swift import CoreData final class CoreDataStack { static let shared = CoreDataStack() private let containerName = "DataModel" lazy var persistentContainer: NSPersistentContainer = { let container = NSPersistentContainer(name: containerName) // Configure store description guard let description = container.persistentStoreDescriptions.first else { fatalError("Failed to retrieve store description") } // Enable persistent history tracking description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey) description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey) // Load stores container.loadPersistentStores { storeDescription, error in if let error = error as NSError? { // Handle error appropriately in production fatalError("Unresolved error \(error), \(error.userInfo)") } } // Configure view context container.viewContext.automaticallyMergesChangesFromParent = true container.viewContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy container.viewContext.name = "ViewContext" return container }() var viewContext: NSManagedObjectContext { persistentContainer.viewContext } func newBackgroundContext() -> NSManagedObjectContext { let context = persistentContainer.newBackgroundContext() context.name = "BackgroundContext" context.transactionAuthor = "BackgroundAuthor" context.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy context.automaticallyMergesChangesFromParent = true return context } func performBackgroundTask(_ block: @escaping (NSManagedObjectContext) -> Void) { persistentContainer.performBackgroundTask { context in context.name = "BackgroundTask" context.transactionAuthor = "BackgroundTaskAuthor" context.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy block(context) } } private init() {} } // Usage let context = CoreDataStack.shared.viewContext // Background work CoreDataStack.shared.performBackgroundTask { context in // Heavy work here try? context.save() } ``` -------------------------------- ### Install Core Data Expert Skill in Claude Code Source: https://github.com/avdlee/core-data-agent-skill/blob/main/README.md Command to install the core-data-expert skill from the AvdLee/Core-Data-Agent-Skill marketplace for personal use in Claude Code. ```bash /plugin install core-data-expert@core-data-agent-skill ``` -------------------------------- ### Install Core Data Agent Skill as Claude Code Plugin (Personal) Source: https://context7.com/avdlee/core-data-agent-skill/llms.txt Commands to install the skill as a personal Claude Code Plugin. This allows direct integration within the Claude environment. ```bash /plugin marketplace add AvdLee/Core-Data-Agent-Skill /plugin install core-data-expert@core-data-agent-skill ``` -------------------------------- ### Install skill-creator skill Source: https://github.com/avdlee/core-data-agent-skill/blob/main/CONTRIBUTING.md Use this command to add the skill-creator skill to your Claude-compatible tool if you are using skills.sh. ```bash npx skills add https://github.com/anthropics/skills --skill skill-creator ``` -------------------------------- ### In-Memory Core Data Test Setup Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/testing.md Sets up an in-memory persistent store for fast and isolated Core Data tests. Requires `XCTest`. ```swift class CoreDataTestCase: XCTestCase { var container: NSPersistentContainer! var context: NSManagedObjectContext! override func setUp() { super.setUp() container = NSPersistentContainer(name: "Model", managedObjectModel: Self.sharedModel) let description = NSPersistentStoreDescription() description.type = NSInMemoryStoreType container.persistentStoreDescriptions = [description] container.loadPersistentStores { description, error in XCTAssertNil(error) } context = container.viewContext } override func tearDown() { context = nil container = nil super.tearDown() } } ``` -------------------------------- ### Example SQL Debug Output Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/fetch-requests.md Sample output from enabling SQL debug, showing the generated SQL query executed by Core Data. ```sql CoreData: sql: SELECT Z_PK, ZNAME, ZVIEWS FROM ZARTICLE WHERE ZVIEWS > ? ORDER BY ZCREATIONDATE DESC LIMIT 20 ``` -------------------------------- ### Basic Core Data Setup with CloudKit Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/cloudkit-integration.md Initializes an NSPersistentCloudKitContainer for Core Data and loads its persistent stores. Ensure CloudKit capability is enabled in Xcode and your Core Data model. ```swift import CoreData import CloudKit let container = NSPersistentCloudKitContainer(name: "Model") container.loadPersistentStores { description, error in if let error = error { fatalError("Failed to load store: \(error)") } } ``` -------------------------------- ### Enable Lightweight Migration with NSPersistentStoreDescription Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/migration.md Lightweight migration is enabled by default when configuring NSPersistentStoreDescription. No explicit setup is required. ```swift let description = NSPersistentStoreDescription(url: storeURL) // Lightweight migration enabled by default ``` -------------------------------- ### Blocking Behavior Example Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/threading.md Demonstrates how `performAndWait` on a background context can still block the main thread if called from it, leading to UI unresponsiveness. ```swift // Called from main thread let backgroundContext = container.newBackgroundContext() // This BLOCKS the main thread! backgroundContext.performAndWait { // Heavy work here blocks UI for i in 0..<10000 { let article = Article(context: backgroundContext) } } ``` -------------------------------- ### Production-Ready Core Data Stack Setup Source: https://context7.com/avdlee/core-data-agent-skill/llms.txt Configure a production-ready `NSPersistentContainer` subclass with merge policies, persistent history tracking, and named contexts. Supports both singleton and dependency-injection patterns. ```swift import CoreData // Production-ready custom container subclass final class CoreDataStack { static let shared = CoreDataStack() lazy var persistentContainer: NSPersistentContainer = { let container = NSPersistentContainer(name: "DataModel") guard let description = container.persistentStoreDescriptions.first else { fatalError("Failed to retrieve store description") } // Required for batch operations and app extensions description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey) description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey) container.loadPersistentStores { error in if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") } } container.viewContext.automaticallyMergesChangesFromParent = true container.viewContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy // Required for constraints container.viewContext.name = "ViewContext" return container }() var viewContext: NSManagedObjectContext { persistentContainer.viewContext } func newBackgroundContext() -> NSManagedObjectContext { let ctx = persistentContainer.newBackgroundContext() ctx.name = "BackgroundContext" ctx.transactionAuthor = "BackgroundAuthor" // Required for persistent history filtering ctx.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy ctx.automaticallyMergesChangesFromParent = true return ctx } private init() {} } // SwiftUI integration @main struct MyApp: App { var body: some Scene { WindowGroup { ContentView() .environment(\.managedObjectContext, CoreDataStack.shared.viewContext) } } } // Async/await bridge for store loading (iOS 15+) extension NSPersistentContainer { func loadPersistentStores() async throws { try await withCheckedThrowingContinuation { continuation in self.loadPersistentStores { error in if let error { continuation.resume(throwing: error) } else { continuation.resume(returning: ()) } } } } } ``` -------------------------------- ### Example Migration Debug Output Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/migration.md Illustrates the typical output seen when Core Data migration debugging is enabled. This helps in understanding the migration steps and identifying potential problems. ```text CoreData: annotation: Migration: Migrating from version 1 to version 2 CoreData: annotation: Migration: Inferred mapping model CoreData: annotation: Migration: Completed successfully ``` -------------------------------- ### SwiftUI App Setup with Core Data Environment Object Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/stack-setup.md This code demonstrates how to integrate a Core Data stack into a SwiftUI application using the environment object pattern. It sets up the `managedObjectContext` for the entire app and shows how to fetch and display data in a `ContentView`. ```swift import SwiftUI @main struct MyApp: App { let persistenceController = PersistentContainer.shared var body: some Scene { WindowGroup { ContentView() .environment(\.managedObjectContext, persistenceController.viewContext) } } } // Usage in views struct ContentView: View { @Environment(\.managedObjectContext) private var viewContext @FetchRequest( sortDescriptors: [NSSortDescriptor(keyPath: \Article.name, ascending: true)], animation: .default) private var articles: FetchedResults
var body: some View { List(articles) { article in Text(article.name ?? "") } } } ``` -------------------------------- ### Enable Lightweight Migration with NSPersistentContainer Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/migration.md Lightweight migration is enabled by default when using NSPersistentContainer. No explicit setup is required. ```swift let container = NSPersistentContainer(name: "Model") // Lightweight migration enabled by default ``` -------------------------------- ### Clean History on App Launch Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/persistent-history.md Execute the history cleaning process when the application finishes launching to maintain a clean history from the start. ```swift // Or on app launch func applicationDidFinishLaunching() { cleaner.clean() } ``` -------------------------------- ### Core Data Store Location Options Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/stack-setup.md Provides examples for defining the URL for Core Data stores, including the default location, a custom location within the document directory, and a location for app extensions using App Groups. ```swift // Default location let storeURL = NSPersistentContainer.defaultDirectoryURL() .appendingPathComponent("Model.sqlite") // Custom location let storeURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] .appendingPathComponent("MyApp.sqlite") // App Group (for extensions) let storeURL = FileManager.default.containerURL( forSecurityApplicationGroupIdentifier: "group.com.example.app" )?.appendingPathComponent("Shared.sqlite") ``` -------------------------------- ### Example Usage of Derived Attributes Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/model-configuration.md Illustrates how derived attributes are defined in a managed object subclass and how their values are automatically populated and accessible after saving changes. ```swift class Article: NSManagedObject { @NSManaged var name: String @NSManaged var category: Category? // Derived from category.name @NSManaged var categoryName: String? // Derived from canonical:(name) @NSManaged var searchName: String? } // Usage article.name = "Core Data Best Practices" try context.save() // After save, derived attributes are updated print(article.searchName) // "core data best practices" print(article.categoryName) // "Swift" ``` -------------------------------- ### Setup UICollectionViewDiffableDataSource Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/fetch-requests.md Initialize a UICollectionViewDiffableDataSource for modern data management in collection views. This involves configuring the cell provider to dequeue and configure cells for displaying data objects. ```swift class ArticlesViewController: UICollectionViewController { private var dataSource: UICollectionViewDiffableDataSource! private var fetchedResultsController: NSFetchedResultsController
! func setupDataSource() { dataSource = UICollectionViewDiffableDataSource( collectionView: collectionView ) { collectionView, indexPath, objectID in let cell = collectionView.dequeueReusableCell( withReuseIdentifier: "ArticleCell", for: indexPath ) as! ArticleCell if let article = try? self.viewContext.existingObject(with: objectID) as? Article { cell.configure(with: article) } return cell } } func setupFetchedResultsController() { let fetchRequest = Article.fetchRequest() fetchRequest.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)] fetchedResultsController = NSFetchedResultsController( fetchRequest: fetchRequest, managedObjectContext: viewContext, sectionNameKeyPath: nil, cacheName: nil ) fetchedResultsController.delegate = self try? fetchedResultsController.performFetch() } } extension ArticlesViewController: NSFetchedResultsControllerDelegate { func controller(_ controller: NSFetchedResultsController, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) { let snapshot = snapshot as NSDiffableDataSourceSnapshot dataSource.apply(snapshot, animatingDifferences: true) } } ``` -------------------------------- ### Analyze SQL Query Output Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/performance.md Example of SQL debug log output. Examine query statements, execution times, and the number of queries to detect issues like the N+1 problem. ```sql CoreData: sql: SELECT Z_PK, ZNAME FROM ZARTICLE WHERE ZVIEWS > ? LIMIT 20 CoreData: annotation: sql execution time: 0.0023s ``` -------------------------------- ### Deleting Files in prepareForDeletion Pitfall Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/model-configuration.md Shows an example of the incorrect practice of deleting files directly within `prepareForDeletion`. This is unsafe because the deletion operation might be rolled back by Core Data. ```swift override func prepareForDeletion() { super.prepareForDeletion() // ❌ Bad: Deletion might be rolled back try? FileManager.default.removeItem(at: fileURL) } ``` -------------------------------- ### Core Data Aggregate Fetch Request Source: https://context7.com/avdlee/core-data-agent-skill/llms.txt Perform aggregate queries using `NSExpressionDescription` to calculate sums and group results. This example calculates the total views per category. ```swift // Aggregate: sum of views per category let categoryExpr = NSExpressionDescription() categoryExpr.name = "categoryName" categoryExpr.expression = NSExpression(forKeyPath: "category.name") categoryExpr.expressionResultType = .stringAttributeType let sumExpr = NSExpressionDescription() sumExpr.name = "totalViews" sumExpr.expression = NSExpression(format: "@sum.views") sumExpr.expressionResultType = .integer64AttributeType let aggRequest = Article.fetchRequest() aggRequest.resultType = .dictionaryResultType aggRequest.propertiesToFetch = [categoryExpr, sumExpr] aggRequest.propertiesToGroupBy = ["category.name"] let results = try context.fetch(aggRequest) as! [[String: Any]] results.forEach { print("\($0[\"categoryName\"]!): \($0[\"totalViews\"]!) views") } ``` -------------------------------- ### Deferred Loading Core Data Stack Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/stack-setup.md Implements a deferred loading pattern for the Core Data stack, allowing the app to start before stores are fully loaded. Use this only when necessary due to increased complexity and potential race conditions. ```swift class CoreDataStack { let container: NSPersistentContainer private(set) var isStoreLoaded = false init() { container = NSPersistentContainer(name: "Model") loadStoresInBackground() } private func loadStoresInBackground() { container.loadPersistentStores { [weak self] description, error in if let error = error { print("Failed to load store: \(error)") return } self?.isStoreLoaded = true NotificationCenter.default.post(name: .storeDidLoad, object: nil) } } func waitForStoreLoad() async { guard !isStoreLoaded else { return } await withCheckedContinuation { continuation in let observer = NotificationCenter.default.addObserver( forName: .storeDidLoad, object: nil, queue: nil ) { _ in continuation.resume() } // Check again in case it loaded while setting up observer if self.isStoreLoaded { NotificationCenter.default.removeObserver(observer) continuation.resume() } } } } ``` -------------------------------- ### Setup NSFetchedResultsController Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/fetch-requests.md Configure NSFetchedResultsController for automatic updates in table or collection views. Specify the fetch request, managed object context, and optionally a section name key path and cache name. ```swift class ArticlesViewController: UIViewController { var fetchedResultsController: NSFetchedResultsController
! func setupFetchedResultsController() { let fetchRequest = Article.fetchRequest() fetchRequest.sortDescriptors = [ NSSortDescriptor(key: "creationDate", ascending: false) ] fetchRequest.fetchBatchSize = 20 fetchedResultsController = NSFetchedResultsController( fetchRequest: fetchRequest, managedObjectContext: viewContext, sectionNameKeyPath: nil, cacheName: "ArticlesCache" ) fetchedResultsController.delegate = self try? fetchedResultsController.performFetch() } } ``` -------------------------------- ### NSFetchedResultsController with Diffable Data Source Source: https://context7.com/avdlee/core-data-agent-skill/llms.txt Set up `NSFetchedResultsController` with a diffable data source for automatic UI updates in iOS 13+. This example shows the basic setup for a collection view controller. ```swift // NSFetchedResultsController with diffable data source (iOS 13+) class ArticlesViewController: UICollectionViewController { private var dataSource: UICollectionViewDiffableDataSource! private var frc: NSFetchedResultsController
! func setup() { let req = Article.fetchRequest() req.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)] req.fetchBatchSize = 20 frc = NSFetchedResultsController( fetchRequest: req, managedObjectContext: CoreDataStack.shared.viewContext, sectionNameKeyPath: "category.name", cacheName: "ArticlesCache" ) frc.delegate = self try? frc.performFetch() } } extension ArticlesViewController: NSFetchedResultsControllerDelegate { func controller(_ controller: NSFetchedResultsController, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) { let snap = snapshot as NSDiffableDataSourceSnapshot dataSource.apply(snap, animatingDifferences: true) } } ``` -------------------------------- ### Clone the repository and navigate to the directory Source: https://github.com/avdlee/core-data-agent-skill/blob/main/CONTRIBUTING.md Follow these steps to clone the project repository and set up your local development environment. ```bash git clone https://github.com/avdlee/core-data-agent-skill.git cd core-data-agent-skill ``` -------------------------------- ### Usage of CoreDataStore for Loading Articles Source: https://context7.com/avdlee/core-data-agent-skill/llms.txt Demonstrates how to use the `CoreDataStore` to read articles on the main actor. ```swift // Usage let store = CoreDataStore(persistentContainer: container) @MainActor func loadArticles() throws -> [Article] { try store.read { ctx in try ctx.fetch(Article.fetchRequest()) } } ``` -------------------------------- ### Testing Core Data Fetch Requests with Predicates Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/testing.md Demonstrates how to test Core Data fetch requests by creating sample data, saving it, applying a predicate, and verifying the results. ```swift func testFetchWithPredicate() throws { // Setup TestDataGenerator.createArticle(name: "Swift", views: 100, in: context) TestDataGenerator.createArticle(name: "iOS", views: 50, in: context) try context.save() // Test let fetchRequest = Article.fetchRequest() fetchRequest.predicate = NSPredicate(format: "views > %d", 75) let results = try context.fetch(fetchRequest) // Verify XCTAssertEqual(results.count, 1) XCTAssertEqual(results.first?.name, "Swift") } ``` -------------------------------- ### Enable Deferred Lightweight Migration Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/migration.md Configure a persistent store description to enable deferred lightweight migration. This defers expensive cleanup operations until resources are available. ```swift let description = NSPersistentStoreDescription(url: storeURL) description.setOption( true as NSNumber, forKey: NSPersistentStoreDeferredLightweightMigrationOptionKey ) ``` -------------------------------- ### Incorrectly Setting Transaction Author Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/persistent-history.md This code shows an incorrect setup where the transaction author is set to nil, which prevents filtering transactions by their source. ```swift // Can't filter transactions by source context.transactionAuthor = nil // Bad! ``` -------------------------------- ### CoreDataStore Usage: Read and Delete Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/concurrency.md Demonstrates how to use the `CoreDataStore` pattern for reading data on the main thread and performing delete operations in a background context. ```swift let store = CoreDataStore(persistentContainer: container) // Main thread operations @MainActor func loadArticles() throws -> [Article] { try store.read { let request = Article.fetchRequest() return try context.fetch(request) } } // Background operations func deleteAll() async throws { try await store.performInBackground { let request = Article.fetchRequest() let articles = try context.fetch(request) articles.forEach { context.delete($0) } try context.save() } } ``` -------------------------------- ### Incorrectly Enabling History Tracking Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/persistent-history.md This snippet shows an incomplete setup for persistent history tracking, missing the essential remote change notification option. ```swift // Only this isn't enough description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey) ``` -------------------------------- ### Testing Core Data Relationships Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/testing.md Verifies the setup and integrity of relationships between Core Data entities, specifically testing a one-to-many relationship between `Article` and `Category`. ```swift func testArticleCategoryRelationship() throws { let category = Category(context: context) category.name = "Swift" let article = Article(context: context) article.name = "Test" article.category = category try context.save() XCTAssertEqual(article.category?.name, "Swift") XCTAssertTrue(category.articles?.contains(article) ?? false) } ``` -------------------------------- ### Enable SQL Debug Logging Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/performance.md Use this launch argument to enable detailed SQL logging for Core Data operations. Analyze the output to identify query complexity and execution times. ```bash -com.apple.CoreData.SQLDebug 1 ``` -------------------------------- ### Manual Code Generation for NSManagedObject Subclass Source: https://context7.com/avdlee/core-data-agent-skill/llms.txt Example of manually defining an `NSManagedObject` subclass with `@NSManaged` properties for projects using default `@MainActor` isolation. ```swift // Manual code generation for default @MainActor isolation projects nonisolated class Article: NSManagedObject { @NSManaged public var title: String? @NSManaged public var timestamp: Date? } ``` -------------------------------- ### Fetch Request Optimization Checklist Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/performance.md A checklist of common fetch request optimizations including batch size, property fetching, prefetching, predicates, fetch limits, and sort descriptors. ```swift let fetchRequest = Article.fetchRequest() // ✅ Set batch size fetchRequest.fetchBatchSize = 20 // ✅ Limit properties fetchRequest.propertiesToFetch = ["name", "views"] // ✅ Prefetch relationships fetchRequest.relationshipKeyPathsForPrefetching = ["category"] // ✅ Use predicate to filter fetchRequest.predicate = NSPredicate(format: "views > %d", 100) // ✅ Set fetch limit if applicable fetchRequest.fetchLimit = 10 // ✅ Specify sort descriptors fetchRequest.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)] ``` -------------------------------- ### Configure Derived Attribute for Current Timestamp Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/model-configuration.md Example of setting a derived attribute to automatically store the current timestamp. This attribute updates on every save. ```swift // Derived attribute: lastModified // Derivation: now() ``` -------------------------------- ### Optimize Fetching: Count vs. Fetch All Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/performance.md Shows how to efficiently count objects without fetching them into memory. Use `context.count(for:)` when only the count is needed. ```swift // Fetches all properties of all objects let articles = try context.fetch(Article.fetchRequest()) let count = articles.count ``` ```swift // Only counts, doesn't fetch objects let count = try context.count(for: Article.fetchRequest()) ``` -------------------------------- ### Persistent History Cleaner Source: https://context7.com/avdlee/core-data-agent-skill/llms.txt Periodically prunes old history entries to prevent unbounded growth. This example deletes history older than 7 days using a timer. ```swift // Cleaner: prune old history to prevent unbounded growth Timer.scheduledTimer(withTimeInterval: 3600, repeats: true) { _ in let ctx = container.newBackgroundContext() ctx.perform { let del = NSPersistentHistoryChangeRequest.deleteHistory( before: Calendar.current.date(byAdding: .day, value: -7, to: Date())! ) try? ctx.execute(del) } } ``` -------------------------------- ### Basic Fetch Request in Swift Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/fetch-requests.md Performs a basic fetch request to retrieve all Article objects from the context. Ensure the context is properly initialized. ```swift let fetchRequest: NSFetchRequest
= Article.fetchRequest() let articles = try context.fetch(fetchRequest) ``` -------------------------------- ### Example Search Query with Canonical String Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/model-configuration.md Shows how to perform a case-insensitive and diacritic-insensitive search using a derived 'canonical' attribute. The predicate uses 'CONTAINS' on the processed string. ```swift // name = "Café" // searchName = "cafe" // Search query fetchRequest.predicate = NSPredicate(format: "searchName CONTAINS %@", "cafe") // Matches "Café", "CAFE", "café", etc. ``` -------------------------------- ### Usage of CoreDataStore for Deleting All Articles Source: https://context7.com/avdlee/core-data-agent-skill/llms.txt Shows how to use the `CoreDataStore` to perform a background operation to delete all articles. ```swift func deleteAll() async throws { try await store.performInBackground { ctx in let articles = try ctx.fetch(Article.fetchRequest()) articles.forEach { ctx.delete($0) } try ctx.save() } } ``` -------------------------------- ### Create a Custom Value Transformer for UIColor Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/model-configuration.md Subclass ValueTransformer to handle the transformation of UIColor to NSData for storage. This example uses NSKeyedArchiver for serialization and NSKeyedUnarchiver for deserialization, requiring NSSecureCoding. ```swift import UIKit @objc(ColorTransformer) class ColorTransformer: ValueTransformer { override class func transformedValueClass() -> AnyClass { return NSData.self } override class func allowsReverseTransformation() -> Bool { return true } override func transformedValue(_ value: Any?) -> Any? { guard let color = value as? UIColor else { return nil } do { let data = try NSKeyedArchiver.archivedData( withRootObject: color, requiringSecureCoding: true ) return data } catch { print("Failed to transform color: \(error)") return nil } } override func reverseTransformedValue(_ value: Any?) -> Any? { guard let data = value as? Data else { return nil } do { let color = try NSKeyedUnarchiver.unarchivedObject( ofClass: UIColor.self, from: data ) return color } catch { print("Failed to reverse transform color: \(error)") return nil } } } ``` -------------------------------- ### Complete Core Data Stack with History Tracking Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/persistent-history.md Sets up a Core Data stack with persistent history tracking enabled. It configures the persistent store, loads it, and initializes history tracking and periodic cleaning. ```swift class CoreDataStack { static let shared = CoreDataStack() lazy var persistentContainer: NSPersistentContainer = { let container = NSPersistentContainer(name: "Model") // Configure store guard let description = container.persistentStoreDescriptions.first else { fatalError("No store description") } // Enable persistent history tracking description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey) description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey) container.loadPersistentStores { description, error in if let error = error { fatalError("Failed to load store: \(error)") } self.setupHistoryTracking(container: container) } // Configure view context container.viewContext.automaticallyMergesChangesFromParent = true container.viewContext.name = "ViewContext" container.viewContext.transactionAuthor = "MainApp" return container }() private var historyObserver: PersistentHistoryObserver? private init() {} private func setupHistoryTracking(container: NSPersistentContainer) { historyObserver = PersistentHistoryObserver(container: container, viewContext: container.viewContext) cleanHistoryPeriodically(container: container) } private func cleanHistoryPeriodically(container: NSPersistentContainer) { Timer.scheduledTimer(withTimeInterval: 3600, repeats: true) { _ in let context = container.newBackgroundContext() let cleaner = PersistentHistoryCleaner( context: context, targets: [.mainApp, .shareExtension] ) cleaner.clean() } } } ``` -------------------------------- ### Configure Derived Attribute for Sum of Related Values Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/model-configuration.md Example of configuring a derived attribute to calculate the sum of a property from related objects. This is useful for aggregating data from one-to-many relationships. ```swift // Derived attribute: totalViews // Derivation: @sum.articles.views ``` -------------------------------- ### Correct Approach to Saving Core Data Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/saving.md Demonstrates the correct approach to saving Core Data, including checking for changes, handling errors, and using the correct thread for background contexts. ```swift // Check for changes guard context.hasPersistentChanges else { return } // Handle errors do { try context.save() } catch { print("Save failed: \(error)") // Handle appropriately } // Use correct thread context.perform { try? context.save() } ``` -------------------------------- ### Correct Approach for Batch Operations Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/batch-operations.md To correctly perform batch operations, enable persistent history tracking on the store description, use a background context for execution, and then process the history changes to update the UI. ```swift // 1. Enable persistent history tracking description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey) // 2. Use background context let context = container.newBackgroundContext() // 3. Execute batch operation context.perform { let batchInsert = NSBatchInsertRequest(entity: Article.entity()) { object in guard let article = object as? Article else { return true } article.name = "Valid Name" return false } try? context.execute(batchInsert) } // 4. UI updates via persistent history tracking ``` -------------------------------- ### Observe CloudKit Sync Events Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/cloudkit-integration.md Registers an observer to receive notifications for CloudKit container events, such as setup, import, and export. Handles success or failure of these events and logs any associated errors. ```swift NotificationCenter.default.addObserver( self, selector: #selector(storeDidChange), name: NSPersistentCloudKitContainer.eventChangedNotification, object: container ) @objc func storeDidChange(_ notification: Notification) { guard let event = notification.userInfo?[NSPersistentCloudKitContainer.eventNotificationUserInfoKey] as? NSPersistentCloudKitContainer.Event else { return } switch event.type { case .setup: print("Setup: \(event.succeeded ? "succeeded" : "failed")") case .import: print("Import: \(event.succeeded ? "succeeded" : "failed")") case .export: print("Export: \(event.succeeded ? "succeeded" : "failed")") @unknown default: break } if let error = event.error { print("Error: \(error)") } } ``` -------------------------------- ### Schedule and Handle Deferred Migration with BackgroundTasks Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/migration.md Integrates deferred migration with the BackgroundTasks framework. This includes registering the task, scheduling it, and handling the migration within the background task context. ```swift import BackgroundTasks // Register task BGTaskScheduler.shared.register( forTaskWithIdentifier: "com.example.app.migration", using: nil ) { task in self.handleMigrationTask(task as! BGProcessingTask) } // Schedule task func scheduleMigration() { let request = BGProcessingTaskRequest(identifier: "com.example.app.migration") request.requiresNetworkConnectivity = false request.requiresExternalPower = false try? BGTaskScheduler.shared.submit(request) } // Handle task func handleMigrationTask(_ task: BGProcessingTask) { task.expirationHandler = { task.setTaskCompleted(success: false) } finishDeferredMigration() task.setTaskCompleted(success: true) } ``` -------------------------------- ### Manually Configure Lightweight Migration Options Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/migration.md Manually enable automatic lightweight migration by setting NSMigratePersistentStoresAutomaticallyOption and NSInferMappingModelAutomaticallyOption when adding a persistent store. ```swift let options = [ NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true ] try coordinator.addPersistentStore( ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: options ) ``` -------------------------------- ### Async/Await Fetching with Core Data Source: https://context7.com/avdlee/core-data-agent-skill/llms.txt Illustrates fetching Core Data objects asynchronously using Swift's `async/await` syntax, suitable for iOS 15+. ```swift // ✅ async/await (iOS 15+) func fetchArticles() async throws -> [Article] { let ctx = CoreDataStack.shared.newBackgroundContext() return try await ctx.perform { try ctx.fetch(Article.fetchRequest()) } } ``` -------------------------------- ### Core Data Save Crash Example Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/model-configuration.md Illustrates a common pitfall where saving a context with constraint violations (like duplicate names) without a proper merge policy will cause a crash. ```swift // Constraint violation will crash let category = Category(context: context) category.name = "Duplicate" try context.save() // CRASH! ``` -------------------------------- ### Configure Background Context Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/stack-setup.md Set up a background context for heavy work on private queues. Always wrap operations in `perform { }` and enable automatic merging from the parent. Setting a transaction author is recommended for persistent history tracking. ```swift override func newBackgroundContext() -> NSManagedObjectContext { let context = super.newBackgroundContext() context.name = "BackgroundContext" context.transactionAuthor = "BackgroundAuthor" context.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy context.automaticallyMergesChangesFromParent = true return context } // Usage let context = container.newBackgroundContext() context.perform { // Heavy work here try? context.save() } ``` -------------------------------- ### Filter Persistent History Transactions by Author Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/stack-setup.md Example demonstrating how to filter persistent history transactions by author, useful for distinguishing changes made by different parts of an application or app extensions. ```swift // Main app mainContext.transactionAuthor = "MainApp" // Share extension shareContext.transactionAuthor = "ShareExtension" // Filter transactions by author let fetchRequest = NSPersistentHistoryChangeRequest.fetchHistory(after: lastToken) if let historyFetch = fetchRequest as? NSPersistentHistoryChangeRequest { historyFetch.fetchRequest?.predicate = NSPredicate( format: "author != %@", "MainApp" ) } ``` -------------------------------- ### Finish Deferred Lightweight Migration Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/migration.md Execute the deferred lightweight migration process. This should be called when resources are available to complete any pending cleanup tasks. ```swift func finishDeferredMigration() { let coordinator = container.persistentStoreCoordinator do { try coordinator.finishDeferredLightweightMigration() print("Deferred migration completed") } catch { print("Failed to finish deferred migration: \(error)") } } ``` -------------------------------- ### Configure Derived Attribute for Relationship Count Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/model-configuration.md Example of configuring a derived attribute in the Data Model Editor to count related objects. This is more efficient than accessing the relationship's count directly. ```swift // In Data Model Editor: // Derived attribute: articlesCount // Derivation: articles.@count ``` -------------------------------- ### Solve N+1 Query Problem with Prefetching Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/performance.md Demonstrates how to resolve the N+1 query problem by prefetching relationship data. This avoids firing individual faults for each accessed relationship. ```swift // Fetches articles let articles = try context.fetch(Article.fetchRequest()) // Each access fires a fault (N queries) for article in articles { print(article.category?.name) // Fault! } ``` ```swift let fetchRequest = Article.fetchRequest() fetchRequest.relationshipKeyPathsForPrefetching = ["category"] let articles = try context.fetch(fetchRequest) // No faults fired for article in articles { print(article.category?.name) // Already loaded } ``` -------------------------------- ### Avoid Fetching All Properties (Swift) Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/fetch-requests.md Demonstrates an inefficient way to fetch data by retrieving all properties of all objects. Use this pattern only when necessary. ```swift // Bad: Fetches all properties, all objects let articles = try context.fetch(Article.fetchRequest()) let count = articles.count ``` -------------------------------- ### Add Marketplace for Claude Code Plugin Source: https://github.com/avdlee/core-data-agent-skill/blob/main/README.md Command to add the AvdLee/Core-Data-Agent-Skill marketplace for personal use within Claude Code. ```bash /plugin marketplace add AvdLee/Core-Data-Agent-Skill ``` -------------------------------- ### Get Model Version Checksum from Xcode Build Log Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/migration.md Retrieve the version checksum for a Core Data model from the Xcode build log, typically found after the 'Compile data model' step. ```text Compile data model Model.xcdatamodeld version checksum: ABC123... ``` -------------------------------- ### Handle Core Data Validation Errors Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/model-configuration.md Catch and handle specific validation errors during Core Data save operations. This example demonstrates checking the error domain and code to provide user-friendly messages. ```swift do { try context.save() } catch let error as NSError { if error.domain == NSCocoaErrorDomain { switch error.code { case NSValidationStringTooShortError: print("String too short") case NSValidationStringTooLongError: print("String too long") case NSManagedObjectValidationError: print("Validation failed") default: print("Other error: \(error.localizedDescription)") } } } ``` -------------------------------- ### Correct Pattern: Use perform (async) Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/threading.md For heavy operations that should not block the UI, use the asynchronous `perform` method instead of `performAndWait`. ```swift // Use perform (async) instead of performAndWait backgroundContext.perform { // Heavy work doesn't block UI } ``` -------------------------------- ### Background Fetch and Main Thread UI Update Source: https://context7.com/avdlee/core-data-agent-skill/llms.txt Shows how to perform a Core Data fetch in a background context and update the UI on the main thread using a completion handler. ```swift // ✅ Background fetch → main thread UI update func loadArticles(completion: @escaping ([Article]) -> Void) { let ctx = CoreDataStack.shared.newBackgroundContext() ctx.perform { let ids = (try? ctx.fetch(Article.fetchRequest()))?.map { $0.objectID } ?? [] DispatchQueue.main.async { let viewCtx = CoreDataStack.shared.viewContext completion(ids.compactMap { try? viewCtx.existingObject(with: $0) as? Article }) } } } ``` -------------------------------- ### Avoid Not Naming Core Data Contexts Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/stack-setup.md This example shows the consequence of not naming Core Data contexts, which can make debugging difficult when issues arise, as it's hard to identify which context is causing problems. ```swift // Hard to debug which context has issues let context = container.newBackgroundContext() // No name, no transaction author ``` -------------------------------- ### Debounce Auto-Save for Text Field Changes Source: https://context7.com/avdlee/core-data-agent-skill/llms.txt Implement debouncing for auto-saves triggered by text field changes to prevent excessive saves. This example uses `DispatchWorkItem` to delay the save operation by 2 seconds after the last change. ```swift // ✅ Debounced auto-save (avoids saving on every keystroke) private var saveWorkItem: DispatchWorkItem? func textFieldDidChange(_ textField: UITextField) { article.name = textField.text saveWorkItem?.cancel() let work = DispatchWorkItem { [weak self] in try? self?.context.saveIfNeeded() } saveWorkItem = work DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: work) } ``` -------------------------------- ### Batch Import with Periodic Saves and Memory Management Source: https://context7.com/avdlee/core-data-agent-skill/llms.txt Perform batch imports efficiently by using a background context and periodic saves. Resetting the context after every 100 items helps control memory usage during large imports. ```swift // ✅ Batch import with periodic saves to control memory func importArticles(_ articles: [ArticleData]) { let ctx = CoreDataStack.shared.newBackgroundContext() ctx.perform { for (i, data) in articles.enumerated() { let article = Article(context: ctx) article.name = data.name if i % 100 == 0 { try? ctx.saveIfNeeded() ctx.reset() // Free memory } } try? ctx.saveIfNeeded() } } ``` -------------------------------- ### Manual Merging of Core Data Context Changes Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/threading.md Provides an example of manually merging changes from a background context into the main view context. This involves observing the `NSManagedObjectContextDidSave` notification and calling `mergeChanges(fromContextDidSave:)` on the main context. ```swift NotificationCenter.default.addObserver( self, selector: #selector(contextDidSave), name: .NSManagedObjectContextDidSave, object: backgroundContext ) @objc func contextDidSave(_ notification: Notification) { viewContext.perform { viewContext.mergeChanges(fromContextDidSave: notification) } } ``` -------------------------------- ### Custom NSPersistentContainer Subclass Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/stack-setup.md Create a custom subclass of NSPersistentContainer to encapsulate stack configuration. This approach enhances organization and testability compared to configuring directly in AppDelegate. It includes setup for persistent history tracking and remote change notifications. ```swift import CoreData class PersistentContainer: NSPersistentContainer { static let shared = PersistentContainer(name: "DataModel") private override init(name: String, managedObjectModel model: NSManagedObjectModel) { super.init(name: name, managedObjectModel: model) configure() } convenience init(name: String) { guard let modelURL = Bundle.main.url(forResource: name, withExtension: "momd"), let model = NSManagedObjectModel(contentsOf: modelURL) else { fatalError("Failed to load data model") } self.init(name: name, managedObjectModel: model) } private func configure() { // Set merge policy for constraint handling viewContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy // Enable automatic merging from parent viewContext.automaticallyMergesChangesFromParent = true // Name the view context for debugging viewContext.name = "ViewContext" // Configure store options before loading configureStoreDescription() // Load persistent stores loadPersistentStores { description, error in if let error = error { // Handle error appropriately fatalError("Failed to load persistent store: \(error)") } } } private func configureStoreDescription() { guard let description = persistentStoreDescriptions.first else { return } description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey) description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey) } } ``` -------------------------------- ### Enable Concurrency Debugging Source: https://context7.com/avdlee/core-data-agent-skill/llms.txt Provides the launch argument to enable Core Data's concurrency debugging features. ```swift // ✅ Enable concurrency debugging (add as launch argument) // -com.apple.CoreData.ConcurrencyDebug 1 ``` -------------------------------- ### Configure Project for Core Data Agent Skill in Claude Code Source: https://github.com/avdlee/core-data-agent-skill/blob/main/README.md JSON configuration for `.claude/settings.json` to automatically provide the Core Data Agent Skill to team members working in a repository. This enables the skill and specifies its source. ```json { "enabledPlugins": { "core-data-expert@core-data-agent-skill": true }, "extraKnownMarketplaces": { "core-data-agent-skill": { "source": { "source": "github", "repo": "AvdLee/Core-Data-Agent-Skill" } } } } ``` -------------------------------- ### Background Import Pattern Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/threading.md A common pattern for importing data in the background using a new background context to avoid blocking the main thread. ```swift func importArticles(_ data: [ArticleData]) { let backgroundContext = container.newBackgroundContext() backgroundContext.perform { for item in data { let article = Article(context: backgroundContext) article.name = item.name article.content = item.content } do { try backgroundContext.save() } catch { print("Failed to save: \(error)") } } } ``` -------------------------------- ### SQL Impact of propertiesToFetch Source: https://github.com/avdlee/core-data-agent-skill/blob/main/core-data-expert/references/fetch-requests.md Illustrates the difference in generated SQL when using `propertiesToFetch` compared to fetching all columns. ```sql -- Without propertiesToFetch SELECT * FROM ZARTICLE ``` ```sql -- With propertiesToFetch SELECT Z_PK, ZNAME, ZCREATIONDATE FROM ZARTICLE ```