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.
GitHub Overview
swiftlang/swift-corelibs-foundation
The Foundation Project, providing core utilities, internationalization, and OS independence
Topics
Star History
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()
}
}