Core Data

Core Data is Apple's framework for object graph management and persistence, providing a robust solution for iOS and macOS development. Rather than directly manipulating data, Core Data abstracts complex data management by treating data as objects. With powerful features centered around NSManagedObjectContext including memory management, relationship handling, lazy loading, and automatic change tracking, Core Data delivers the reliability and performance required for enterprise-level application development.

ORMSwiftiOSmacOSObject GraphPersistenceApple

GitHub Overview

swiftlang/swift-corelibs-foundation

The Foundation Project, providing core utilities, internationalization, and OS independence

Stars5,374
Watchers312
Forks1,157
Created:November 9, 2015
Language:C
License:Apache License 2.0

Topics

None

Star History

swiftlang/swift-corelibs-foundation Star History
Data as of: 7/19/2025, 08:07 AM

Library

Core Data

Overview

Core Data is Apple's framework for object graph management and persistence, providing a robust solution for iOS and macOS development. Rather than directly manipulating data, Core Data abstracts complex data management by treating data as objects. With powerful features centered around NSManagedObjectContext including memory management, relationship handling, lazy loading, and automatic change tracking, Core Data delivers the reliability and performance required for enterprise-level application development.

Details

Core Data 2025 maintains its strong position in enterprise application development even after the introduction of SwiftData. The introduction of NSPersistentContainer from iOS 10 significantly simplified Core Data stack management and improved integration with modern Swift development patterns. Complete SwiftUI integration enables declarative data binding through @FetchRequest, @ObservedObject, and environment variables. It comprehensively supports advanced features necessary for large-scale data processing including CloudKit auto-synchronization, background context processing, batch operations, and SQLite store optimization.

Key Features

  • Object Graph Management: Automatic management of relationships and dependencies between entities
  • Lazy Loading: Memory efficiency through data loading only when needed
  • Automatic Change Tracking: Automatic detection of object changes and execution of only necessary updates
  • Background Processing: Asynchronous data operations that don't block the main thread
  • CloudKit Integration: Automatic data synchronization across devices and cloud backup
  • SwiftUI Integration: Seamless data binding with declarative UI

Pros and Cons

Pros

  • Long-term support and stability guarantee as Apple's official framework
  • Improved development efficiency through automatic management of complex object graphs and relationships
  • Simple setup and management with NSPersistentContainer
  • Easy cloud synchronization and multi-device support through CloudKit integration
  • Modern development experience with complete SwiftUI integration
  • Rich documentation and community support
  • Enterprise-level performance and reliability

Cons

  • High learning curve with complex architecture challenging for beginners
  • Limited to Apple platforms, unsuitable for cross-platform development
  • More verbose compared to SwiftData, requiring boilerplate code
  • Debugging can be difficult, and performance issue identification may be time-consuming
  • Complex migration work when changing data models
  • May be feature-heavy for small projects, potentially leading to design complexity

Reference Pages

Code Examples

Project Setup and Basic Configuration

// AppDelegate.swift or App.swift
import UIKit
import CoreData

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    // MARK: - Core Data Stack
    lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "DataModel")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                // Implement proper error handling for production
                fatalError("Core Data error: \(error), \(error.userInfo)")
            }
        })
        return container
    }()
    
    // MARK: - Core Data Saving Support
    func saveContext() {
        let context = persistentContainer.viewContext
        
        if context.hasChanges {
            do {
                try context.save()
            } catch {
                let nsError = error as NSError
                fatalError("Core Data save error: \(nsError), \(nsError.userInfo)")
            }
        }
    }
}

// When using SwiftUI App
@main
struct MyApp: App {
    let persistenceController = PersistenceController.shared
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.managedObjectContext, persistenceController.container.viewContext)
        }
    }
}

// PersistenceController.swift
import CoreData

struct PersistenceController {
    static let shared = PersistenceController()
    
    let container: NSPersistentContainer
    
    init(inMemory: Bool = false) {
        container = NSPersistentContainer(name: "DataModel")
        
        if inMemory {
            container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
        }
        
        container.loadPersistentStores { _, error in
            if let error = error as NSError? {
                fatalError("Core Data error: \(error), \(error.userInfo)")
            }
        }
        
        // Auto-save configuration
        container.viewContext.automaticallyMergesChangesFromParent = true
    }
}

Data Model Definition and Entity Creation

// User+CoreDataClass.swift
import Foundation
import CoreData

@objc(User)
public class User: NSManagedObject {
    
    convenience init(context: NSManagedObjectContext, name: String, email: String) {
        self.init(context: context)
        self.name = name
        self.email = email
        self.createdAt = Date()
    }
    
    // Computed property example
    var displayName: String {
        return name ?? "Unknown User"
    }
    
    // Validation
    public override func validateForInsert() throws {
        try super.validateForInsert()
        try validateUserData()
    }
    
    public override func validateForUpdate() throws {
        try super.validateForUpdate()
        try validateUserData()
    }
    
    private func validateUserData() throws {
        guard let name = name, !name.isEmpty else {
            throw ValidationError.emptyName
        }
        
        guard let email = email, email.contains("@") else {
            throw ValidationError.invalidEmail
        }
    }
}

// User+CoreDataProperties.swift
import Foundation
import CoreData

extension User {
    
    @nonobjc public class func fetchRequest() -> NSFetchRequest<User> {
        return NSFetchRequest<User>(entityName: "User")
    }
    
    @NSManaged public var name: String?
    @NSManaged public var email: String?
    @NSManaged public var createdAt: Date?
    @NSManaged public var posts: NSSet?
}

// MARK: - Generated accessors for posts
extension User {
    
    @objc(addPostsObject:)
    @NSManaged public func addToPosts(_ value: Post)
    
    @objc(removePostsObject:)
    @NSManaged public func removeFromPosts(_ value: Post)
    
    @objc(addPosts:)
    @NSManaged public func addToPosts(_ values: NSSet)
    
    @objc(removePosts:)
    @NSManaged public func removeFromPosts(_ values: NSSet)
}

// ValidationError.swift
enum ValidationError: Error, LocalizedError {
    case emptyName
    case invalidEmail
    
    var errorDescription: String? {
        switch self {
        case .emptyName:
            return "Name is required"
        case .invalidEmail:
            return "Please enter a valid email address"
        }
    }
}

CRUD Operations and Query Execution

// CoreDataManager.swift
import Foundation
import CoreData

class CoreDataManager {
    static let shared = CoreDataManager()
    
    private let persistentContainer: NSPersistentContainer
    
    private init() {
        persistentContainer = PersistenceController.shared.container
    }
    
    var context: NSManagedObjectContext {
        return persistentContainer.viewContext
    }
    
    // MARK: - Create
    func createUser(name: String, email: String) -> User {
        let user = User(context: context, name: name, email: email)
        saveContext()
        return user
    }
    
    func createPost(title: String, content: String, author: User) -> Post {
        let post = Post(context: context)
        post.title = title
        post.content = content
        post.author = author
        post.createdAt = Date()
        saveContext()
        return post
    }
    
    // MARK: - Read
    func fetchAllUsers() -> [User] {
        let request: NSFetchRequest<User> = User.fetchRequest()
        request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
        
        do {
            return try context.fetch(request)
        } catch {
            print("User fetch error: \(error)")
            return []
        }
    }
    
    func fetchUsers(matching predicate: NSPredicate, limit: Int? = nil) -> [User] {
        let request: NSFetchRequest<User> = User.fetchRequest()
        request.predicate = predicate
        request.sortDescriptors = [NSSortDescriptor(key: "createdAt", ascending: false)]
        
        if let limit = limit {
            request.fetchLimit = limit
        }
        
        do {
            return try context.fetch(request)
        } catch {
            print("Conditional user fetch error: \(error)")
            return []
        }
    }
    
    func fetchUserByEmail(_ email: String) -> User? {
        let predicate = NSPredicate(format: "email == %@", email)
        return fetchUsers(matching: predicate, limit: 1).first
    }
    
    // MARK: - Update
    func updateUser(_ user: User, name: String? = nil, email: String? = nil) {
        if let name = name {
            user.name = name
        }
        if let email = email {
            user.email = email
        }
        saveContext()
    }
    
    // MARK: - Delete
    func deleteUser(_ user: User) {
        context.delete(user)
        saveContext()
    }
    
    func deleteAllUsers() {
        let request: NSFetchRequest<NSFetchRequestResult> = User.fetchRequest()
        let deleteRequest = NSBatchDeleteRequest(fetchRequest: request)
        
        do {
            try context.execute(deleteRequest)
            try context.save()
        } catch {
            print("Batch delete error: \(error)")
        }
    }
    
    // MARK: - Save Context
    func saveContext() {
        if context.hasChanges {
            do {
                try context.save()
            } catch {
                print("Save error: \(error)")
            }
        }
    }
}

// Usage example
let manager = CoreDataManager.shared

// Create user
let user = manager.createUser(name: "John Doe", email: "[email protected]")

// Fetch all users
let allUsers = manager.fetchAllUsers()

// Conditional search
let predicate = NSPredicate(format: "name CONTAINS[cd] %@", "John")
let searchResults = manager.fetchUsers(matching: predicate)

// Search by email
if let foundUser = manager.fetchUserByEmail("[email protected]") {
    print("User found: \(foundUser.displayName)")
}

// Update user
manager.updateUser(user, name: "John Smith")

// Delete user
manager.deleteUser(user)

SwiftUI Integration and Reactive Data Binding

// ContentView.swift
import SwiftUI
import CoreData

struct ContentView: View {
    @Environment(\.managedObjectContext) private var viewContext
    
    @FetchRequest(
        entity: User.entity(),
        sortDescriptors: [NSSortDescriptor(keyPath: \User.name, ascending: true)],
        predicate: nil
    ) private var users: FetchedResults<User>
    
    @State private var showingAddUser = false
    @State private var searchText = ""
    
    var filteredUsers: [User] {
        if searchText.isEmpty {
            return Array(users)
        } else {
            return users.filter { user in
                user.name?.localizedCaseInsensitiveContains(searchText) ?? false
            }
        }
    }
    
    var body: some View {
        NavigationView {
            List {
                ForEach(filteredUsers, id: \.objectID) { user in
                    UserRowView(user: user)
                }
                .onDelete(perform: deleteUsers)
            }
            .searchable(text: $searchText, prompt: "Search users")
            .navigationTitle("Users")
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button("Add") {
                        showingAddUser = true
                    }
                }
            }
            .sheet(isPresented: $showingAddUser) {
                AddUserView()
                    .environment(\.managedObjectContext, viewContext)
            }
        }
    }
    
    private func deleteUsers(offsets: IndexSet) {
        withAnimation {
            offsets.map { filteredUsers[$0] }.forEach(viewContext.delete)
            
            do {
                try viewContext.save()
            } catch {
                print("Delete error: \(error)")
            }
        }
    }
}

// UserRowView.swift
struct UserRowView: View {
    @ObservedObject var user: User
    
    var body: some View {
        VStack(alignment: .leading, spacing: 4) {
            Text(user.displayName)
                .font(.headline)
            
            if let email = user.email {
                Text(email)
                    .font(.caption)
                    .foregroundColor(.secondary)
            }
            
            if let createdAt = user.createdAt {
                Text("Created: \(createdAt, formatter: dateFormatter)")
                    .font(.caption2)
                    .foregroundColor(.secondary)
            }
        }
        .padding(.vertical, 2)
    }
    
    private let dateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateStyle = .short
        formatter.timeStyle = .short
        return formatter
    }()
}

// AddUserView.swift
struct AddUserView: View {
    @Environment(\.managedObjectContext) private var viewContext
    @Environment(\.dismiss) private var dismiss
    
    @State private var name = ""
    @State private var email = ""
    @State private var showingAlert = false
    @State private var alertMessage = ""
    
    var body: some View {
        NavigationView {
            Form {
                Section(header: Text("User Information")) {
                    TextField("Name", text: $name)
                    TextField("Email", text: $email)
                        .keyboardType(.emailAddress)
                        .autocapitalization(.none)
                }
            }
            .navigationTitle("New User")
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    Button("Cancel") {
                        dismiss()
                    }
                }
                
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button("Save") {
                        saveUser()
                    }
                    .disabled(name.isEmpty || email.isEmpty)
                }
            }
            .alert("Error", isPresented: $showingAlert) {
                Button("OK") { }
            } message: {
                Text(alertMessage)
            }
        }
    }
    
    private func saveUser() {
        do {
            let newUser = User(context: viewContext, name: name, email: email)
            try viewContext.save()
            dismiss()
        } catch {
            alertMessage = "Failed to save user: \(error.localizedDescription)"
            showingAlert = true
        }
    }
}

Background Processing and Performance Optimization

// BackgroundTaskManager.swift
import Foundation
import CoreData

class BackgroundTaskManager {
    private let persistentContainer: NSPersistentContainer
    
    init(container: NSPersistentContainer) {
        self.persistentContainer = container
    }
    
    // Large data processing in background context
    func performBackgroundTask(_ block: @escaping (NSManagedObjectContext) -> Void) {
        persistentContainer.performBackgroundTask { context in
            // Background context configuration
            context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
            
            block(context)
            
            // Save in background context
            if context.hasChanges {
                do {
                    try context.save()
                } catch {
                    print("Background save error: \(error)")
                }
            }
        }
    }
    
    // Batch processing for large data
    func batchInsertUsers(userData: [[String: Any]], completion: @escaping (Result<Void, Error>) -> Void) {
        performBackgroundTask { context in
            let batchInsert = NSBatchInsertRequest(entity: User.entity()) { (managedObject: NSManagedObject) -> Bool in
                guard !userData.isEmpty else { return true }
                
                let userDict = userData.removeFirst()
                managedObject.setValue(userDict["name"], forKey: "name")
                managedObject.setValue(userDict["email"], forKey: "email")
                managedObject.setValue(Date(), forKey: "createdAt")
                
                return userData.isEmpty
            }
            
            do {
                try context.execute(batchInsert)
                DispatchQueue.main.async {
                    completion(.success(()))
                }
            } catch {
                DispatchQueue.main.async {
                    completion(.failure(error))
                }
            }
        }
    }
    
    // Batch update
    func batchUpdateUsers(predicate: NSPredicate, updates: [String: Any]) {
        performBackgroundTask { context in
            let batchUpdate = NSBatchUpdateRequest(entityName: "User")
            batchUpdate.predicate = predicate
            batchUpdate.propertiesToUpdate = updates
            batchUpdate.resultType = .updatedObjectsCountResultType
            
            do {
                let result = try context.execute(batchUpdate) as? NSBatchUpdateResult
                print("Updated objects count: \(result?.result ?? 0)")
            } catch {
                print("Batch update error: \(error)")
            }
        }
    }
    
    // Memory-efficient fetching
    func fetchUsersEfficiently(batchSize: Int = 20, completion: @escaping ([User]) -> Void) {
        performBackgroundTask { context in
            let request: NSFetchRequest<User> = User.fetchRequest()
            request.fetchBatchSize = batchSize
            request.returnsObjectsAsFaults = false
            
            do {
                let users = try context.fetch(request)
                DispatchQueue.main.async {
                    completion(users)
                }
            } catch {
                print("Efficient fetch error: \(error)")
                DispatchQueue.main.async {
                    completion([])
                }
            }
        }
    }
}

// Usage example
let backgroundManager = BackgroundTaskManager(container: persistentContainer)

// Insert large amount of data
let userData = [
    ["name": "User 1", "email": "[email protected]"],
    ["name": "User 2", "email": "[email protected]"],
    // ... other user data
]

backgroundManager.batchInsertUsers(userData: userData) { result in
    switch result {
    case .success:
        print("Batch insert completed")
    case .failure(let error):
        print("Batch insert error: \(error)")
    }
}

// Conditional batch update
let predicate = NSPredicate(format: "createdAt < %@", Date().addingTimeInterval(-86400)) // 1 day ago
backgroundManager.batchUpdateUsers(predicate: predicate, updates: ["isActive": false])

CloudKit Integration and Data Synchronization

// CloudKitManager.swift
import Foundation
import CoreData
import CloudKit

class CloudKitManager {
    private let container: NSPersistentCloudKitContainer
    
    init(container: NSPersistentCloudKitContainer) {
        self.container = container
        setupCloudKit()
    }
    
    private func setupCloudKit() {
        // CloudKit notification setup
        container.persistentStoreDescriptions.forEach { description in
            description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
            description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
        }
    }
    
    // Check CloudKit status
    func checkCloudKitStatus() {
        CKContainer.default().accountStatus { [weak self] status, error in
            DispatchQueue.main.async {
                switch status {
                case .available:
                    print("CloudKit account available")
                    self?.enableCloudKitSync()
                case .noAccount:
                    print("No iCloud account")
                case .restricted:
                    print("CloudKit access restricted")
                case .couldNotDetermine:
                    print("CloudKit status unknown")
                case .temporarilyUnavailable:
                    print("CloudKit temporarily unavailable")
                @unknown default:
                    print("CloudKit unknown status")
                }
            }
        }
    }
    
    private func enableCloudKitSync() {
        // Enable CloudKit synchronization
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(contextDidSave),
            name: .NSManagedObjectContextDidSave,
            object: nil
        )
    }
    
    @objc private func contextDidSave(_ notification: Notification) {
        // Post CloudKit sync processing
        print("Context saved - CloudKit syncing")
    }
    
    // Force manual sync
    func forceSyncWithCloudKit() {
        container.performBackgroundTask { context in
            do {
                try context.save()
                print("Manual CloudKit sync completed")
            } catch {
                print("CloudKit sync error: \(error)")
            }
        }
    }
    
    // CloudKit error handling
    func handleCloudKitError(_ error: Error) {
        if let ckError = error as? CKError {
            switch ckError.code {
            case .quotaExceeded:
                print("CloudKit storage limit exceeded")
            case .networkUnavailable:
                print("No network connection")
            case .serviceUnavailable:
                print("CloudKit service unavailable")
            default:
                print("CloudKit error: \(ckError.localizedDescription)")
            }
        }
    }
}

// PersistenceController.swift (CloudKit-enabled version)
struct CloudKitPersistenceController {
    static let shared = CloudKitPersistenceController()
    
    let container: NSPersistentCloudKitContainer
    let cloudKitManager: CloudKitManager
    
    init(inMemory: Bool = false) {
        container = NSPersistentCloudKitContainer(name: "DataModel")
        
        if inMemory {
            container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
        } else {
            // CloudKit configuration
            let storeDescription = container.persistentStoreDescriptions.first!
            storeDescription.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
            storeDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
        }
        
        container.loadPersistentStores { _, error in
            if let error = error as NSError? {
                fatalError("CloudKit Core Data error: \(error), \(error.userInfo)")
            }
        }
        
        container.viewContext.automaticallyMergesChangesFromParent = true
        
        cloudKitManager = CloudKitManager(container: container)
        cloudKitManager.checkCloudKitStatus()
    }
}