swift-core-data

Persist data with Core Data - models, contexts, fetch requests, migrations, SwiftData

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "swift-core-data" with this command: npx skills add pluginagentmarketplace/custom-plugin-swift/pluginagentmarketplace-custom-plugin-swift-swift-core-data

Swift Core Data Skill

Data persistence framework knowledge for Core Data and SwiftData in Apple platforms.

Prerequisites

  • Xcode 15+ installed
  • Understanding of object graphs
  • Basic SQL concepts helpful

Parameters

parameters:
  framework:
    type: string
    enum: [core_data, swift_data]
    default: swift_data
    description: Persistence framework
  cloudkit_sync:
    type: boolean
    default: false
  lightweight_migration:
    type: boolean
    default: true
  store_type:
    type: string
    enum: [sqlite, in_memory, binary]
    default: sqlite

Topics Covered

Core Data vs SwiftData

FeatureCore DataSwiftData
Min iOS3.0+17.0+
Definition.xcdatamodeld@Model macro
ThreadingManual (contexts)Actor-based
FetchNSFetchRequest#Predicate
Learning CurveSteepGentle

Core Data Stack

ComponentPurpose
NSPersistentContainerEncapsulates stack
NSManagedObjectContextWorking area for objects
NSManagedObjectModelSchema definition
NSPersistentStoreCoordinatorStore management

SwiftData Components

ComponentPurpose
ModelContainerSchema + store
ModelContextWorking area
@ModelEntity macro
@QueryFetch in SwiftUI

Code Examples

SwiftData (iOS 17+)

import SwiftData

// MARK: - Model Definition

@Model
final class Task {
    var title: String
    var notes: String
    var dueDate: Date?
    var isCompleted: Bool
    var priority: Priority
    var createdAt: Date

    @Relationship(deleteRule: .cascade, inverse: \Subtask.parentTask)
    var subtasks: [Subtask] = []

    @Relationship(inverse: \Tag.tasks)
    var tags: [Tag] = []

    init(title: String, notes: String = "", dueDate: Date? = nil, priority: Priority = .medium) {
        self.title = title
        self.notes = notes
        self.dueDate = dueDate
        self.isCompleted = false
        self.priority = priority
        self.createdAt = Date()
    }
}

@Model
final class Subtask {
    var title: String
    var isCompleted: Bool
    var parentTask: Task?

    init(title: String, parentTask: Task? = nil) {
        self.title = title
        self.isCompleted = false
        self.parentTask = parentTask
    }
}

@Model
final class Tag {
    @Attribute(.unique) var name: String
    var color: String
    var tasks: [Task] = []

    init(name: String, color: String = "#007AFF") {
        self.name = name
        self.color = color
    }
}

enum Priority: String, Codable, CaseIterable {
    case low, medium, high, urgent
}

// MARK: - Container Setup

@main
struct TaskApp: App {
    let container: ModelContainer

    init() {
        let schema = Schema([Task.self, Subtask.self, Tag.self])
        let config = ModelConfiguration(
            schema: schema,
            isStoredInMemoryOnly: false,
            cloudKitDatabase: .none
        )

        do {
            container = try ModelContainer(for: schema, configurations: config)
        } catch {
            fatalError("Failed to create ModelContainer: \(error)")
        }
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(container)
    }
}

// MARK: - SwiftUI Integration

struct TaskListView: View {
    @Environment(\.modelContext) private var context
    @Query(sort: \Task.dueDate) private var tasks: [Task]

    var body: some View {
        List {
            ForEach(tasks) { task in
                TaskRow(task: task)
            }
            .onDelete(perform: deleteTasks)
        }
    }

    private func deleteTasks(at offsets: IndexSet) {
        for index in offsets {
            context.delete(tasks[index])
        }
    }
}

// Query with predicate
struct IncompleteTasks: View {
    @Query(filter: #Predicate<Task> { !$0.isCompleted },
           sort: [SortDescriptor(\Task.priority, order: .reverse)])
    private var tasks: [Task]

    var body: some View {
        List(tasks) { task in
            Text(task.title)
        }
    }
}

Core Data (Traditional)

import CoreData

// MARK: - Core Data Stack

final class CoreDataStack {
    static let shared = CoreDataStack()

    lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "DataModel")

        container.loadPersistentStores { _, error in
            if let error = error as NSError? {
                fatalError("Core Data error: \(error), \(error.userInfo)")
            }
        }

        container.viewContext.automaticallyMergesChangesFromParent = true
        container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
        return container
    }()

    var viewContext: NSManagedObjectContext {
        persistentContainer.viewContext
    }

    func newBackgroundContext() -> NSManagedObjectContext {
        let context = persistentContainer.newBackgroundContext()
        context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
        return context
    }

    func saveContext() {
        let context = viewContext
        if context.hasChanges {
            do {
                try context.save()
            } catch {
                let nsError = error as NSError
                print("Core Data save error: \(nsError), \(nsError.userInfo)")
            }
        }
    }
}

// MARK: - Fetch Request Helpers

extension NSManagedObjectContext {
    func fetch<T: NSManagedObject>(_ type: T.Type, predicate: NSPredicate? = nil, sortDescriptors: [NSSortDescriptor]? = nil) throws -> [T] {
        let request = T.fetchRequest()
        request.predicate = predicate
        request.sortDescriptors = sortDescriptors
        return try fetch(request) as? [T] ?? []
    }
}

// MARK: - Background Operations

extension CoreDataStack {
    func performBackgroundTask<T>(_ block: @escaping (NSManagedObjectContext) throws -> T) async throws -> T {
        try await withCheckedThrowingContinuation { continuation in
            persistentContainer.performBackgroundTask { context in
                do {
                    let result = try block(context)
                    if context.hasChanges {
                        try context.save()
                    }
                    continuation.resume(returning: result)
                } catch {
                    continuation.resume(throwing: error)
                }
            }
        }
    }

    func batchInsert<T: Encodable>(entities: [T], entityName: String) async throws {
        let context = newBackgroundContext()

        try await context.perform {
            let insertRequest = NSBatchInsertRequest(entityName: entityName, objects: entities.map { entity -> [String: Any] in
                let data = try! JSONEncoder().encode(entity)
                return try! JSONSerialization.jsonObject(with: data) as! [String: Any]
            })
            insertRequest.resultType = .count

            let result = try context.execute(insertRequest) as! NSBatchInsertResult
            print("Inserted \(result.result ?? 0) records")

            // Merge changes to view context
            NSManagedObjectContext.mergeChanges(
                fromRemoteContextSave: [NSInsertedObjectsKey: []],
                into: [self.viewContext]
            )
        }
    }
}

Migration Handling

// Lightweight Migration (automatic)
let container = NSPersistentContainer(name: "DataModel")
let description = container.persistentStoreDescriptions.first
description?.setOption(true as NSNumber, forKey: NSMigratePersistentStoresAutomaticallyOption)
description?.setOption(true as NSNumber, forKey: NSInferMappingModelAutomaticallyOption)

// Custom Migration Manager
final class MigrationManager {
    func requiresMigration(at storeURL: URL, for model: NSManagedObjectModel) -> Bool {
        guard let metadata = try? NSPersistentStoreCoordinator.metadataForPersistentStore(ofType: NSSQLiteStoreType, at: storeURL) else {
            return false
        }
        return !model.isConfiguration(withName: nil, compatibleWithStoreMetadata: metadata)
    }

    func migrateStore(at storeURL: URL, to destinationModel: NSManagedObjectModel) throws {
        // Implementation for custom migration
    }
}

Troubleshooting

Common Issues

IssueCauseSolution
"The model configuration is invalid"Schema mismatchDelete app, check @Model
Context save crashWrong threadUse perform/performAndWait
Relationship faultObject deletedCheck for nil before access
Slow fetchMissing indexAdd index to frequently queried attributes
Migration failsNon-trivial changeWrite custom mapping model

Debug Tips

# Enable Core Data debug logging
# Add to scheme arguments:
-com.apple.CoreData.SQLDebug 1
-com.apple.CoreData.Logging.stderr 1

# SwiftData logging
-com.apple.SwiftData.SQLDebug 1
// Print fetch request SQL
let request: NSFetchRequest<Task> = Task.fetchRequest()
print(request.description)

// Check context state
print("Inserted: \(context.insertedObjects.count)")
print("Updated: \(context.updatedObjects.count)")
print("Deleted: \(context.deletedObjects.count)")

Validation Rules

validation:
  - rule: background_for_heavy_operations
    severity: error
    check: Use background context for batch operations
  - rule: main_thread_for_ui
    severity: error
    check: Only access viewContext on main thread
  - rule: index_frequently_queried
    severity: warning
    check: Add indexes to attributes used in predicates

Usage

Skill("swift-core-data")

Related Skills

  • swift-networking - Syncing remote data
  • swift-swiftui - @Query integration
  • swift-testing - In-memory stores for tests

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Automation

swift-uikit

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

swift-macos

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

swift-architecture

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

swift-spm

No summary provided by upstream source.

Repository SourceNeeds Review