Working with Core Data
1. Setting Up Core Data Stack
Example: Basic Core Data Stack Setup
import CoreData
class CoreDataManager {
static let shared = CoreDataManager()
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "MyModel")
container.loadPersistentStores { _, error in
if let error = error {
fatalError("Core Data error: \(error)")
}
}
return container
}()
var context: NSManagedObjectContext {
return persistentContainer.viewContext
}
}
| Component | Description | Notes |
|---|---|---|
| NSPersistentContainer | Manages the persistent store and context | Created with a model name |
| NSManagedObjectContext | Handles changes to managed objects | Used for read/write operations |
| NSPersistentStoreCoordinator | Coordinates between store and managed objects | Part of the Core Data stack |
.xcdatamodeld file.loadPersistentStores closure.Setup Steps
- Create a
CoreDataManagersingleton - Initialize
NSPersistentContainerwith model name - Load persistent stores in the container
- Access managed object context via
persistentContainer.viewContext
2. Creating Managed Object Model
Managed Object Models (MOM) define the structure of your Core Data entities and their relationships. You can create a MOM in two ways: programmatically or using the Core Data model editor.
| Approach | Description | Use Case |
|---|---|---|
| Programmatic | Define entities, attributes, and relationships using Swift code. | Dynamic model generation or runtime configuration. |
| Model Editor | Use Xcode's visual editor to define entities and relationships. | Static, predefined models with visual design support. |
Example: Programmatic MOM Creation
import CoreData
let model = NSManagedObjectModel()
let entity = NSEntityDescription()
entity.name = "Person"
entity.managedObjectClassName = "Person"
let nameAttribute = NSAttributeDescription()
nameAttribute.name = "name"
nameAttribute.attributeType = .stringAttributeType
nameAttribute.isOptional = false
let ageAttribute = NSAttributeDescription()
ageAttribute.name = "age"
ageAttribute.attributeType = .integer16AttributeType
ageAttribute.isOptional = true
entity.properties = [nameAttribute, ageAttribute]
model.entities = [entity]
let persistentContainer = NSPersistentContainer(name: "MyModel", managedObjectModel: model)
.xcdatamodeld file that contains the model definition.
3. Using NSManagedObjectContext
NSManagedObjectContext is the primary interface for working with Core Data. It manages a collection of managed objects and tracks changes to them.
| Property | Type | Description |
|---|---|---|
| persistentStoreCoordinator | NSPersistentStoreCoordinator? |
The coordinator that manages the persistent stores. |
| parent | NSManagedObjectContext? |
The parent context, if this context is a child context. |
| concurrencyType | NSManagedObjectContextConcurrencyType |
The concurrency type of the context (e.g., MainQueueConcurrencyType, PrivateQueueConcurrencyType). |
| name | String? |
A name for the context, useful for debugging. |
Example: Creating a Managed Object Context
let context = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
context.persistentStoreCoordinator = persistentStoreCoordinator
Example: Fetching Objects
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Person")
do {
let results = try context.fetch(fetchRequest)
for person in results {
print(person.value(forKey: "name") as? String ?? "")
}
} catch {
print("Fetch failed: \(error)")
}
Example: Saving Changes
do {
try context.save()
print("Changes saved successfully")
} catch {
print("Save failed: \(error)")
}
save() is called.
| Method | Description | Return Type |
|---|---|---|
| fetch(_:) | Fetches managed objects from the persistent store. | [NSManagedObject] |
| save() | Saves changes to the persistent store. | Void |
| insertObject(_:) | Inserts a new managed object into the context. | Void |
| delete(_:) | Marks a managed object for deletion. | Void |
4. Creating Entities Programmatically
Entities in Core Data can be created programmatically using the NSEntityDescription and NSAttributeDescription classes. This approach is useful for dynamically generating entities at runtime.
| Property | Type | Description |
|---|---|---|
| name | String |
The name of the entity. |
| managedObjectClassName | String |
The class name of the managed object subclass. |
| attributes | [NSAttributeDescription] |
List of attributes for the entity. |
Example: Creating an Entity Programmatically
import CoreData
let entity = NSEntityDescription()
entity.name = "Person"
entity.managedObjectClassName = "Person"
let nameAttribute = NSAttributeDescription()
nameAttribute.name = "name"
nameAttribute.attributeType = .stringAttributeType
nameAttribute.isOptional = false
let ageAttribute = NSAttributeDescription()
ageAttribute.name = "age"
ageAttribute.attributeType = .integer16AttributeType
ageAttribute.isOptional = true
entity.attributes = [nameAttribute, ageAttribute]
let managedObjectModel = NSManagedObjectModel()
managedObjectModel.entities = [entity]
5. Fetching Data
Example: Basic Fetch Request
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Person")
let context = persistentContainer.viewContext
let results = try context.fetch(fetchRequest)
| Property | Description | Values |
|---|---|---|
| entity | The entity to fetch | NSFetchRequest<Person> |
| predicate | Filter conditions for the fetch | NSPredicate(format: "age >= 18") |
| sortDescriptors | Sorting order for results | [NSSortDescriptor(key: "name", ascending: true)] |
| fetchLimit | Maximum number of results to return | 10 |
try block when fetching data to avoid runtime crashes.Example: Fetch with Predicate
let fetchRequest = NSFetchRequest<Person>(entityName: "Person")
fetchRequest.predicate = NSPredicate(format: "name CONTAINS[cd] %@", "John")
let context = persistentContainer.viewContext
let results = try context.fetch(fetchRequest)
Example: Fetch with Sort
let fetchRequest = NSFetchRequest<Person>(entityName: "Person")
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "age", ascending: false)]
let context = persistentContainer.viewContext
let results = try context.fetch(fetchRequest)
6. Filtering with Predicates
| Filter Type | Description | Example |
|---|---|---|
| NSPredicate | Used to filter Core Data fetch requests | let predicate = NSPredicate(format: "name CONTAINS[cd] %@", searchTerm) |
| format | String-based predicate format | NSPredicate(format: "age > %d AND name BEGINSWITH %@", 18, "J") |
| keyPath | Property to filter on | NSPredicate(format: "department.name == %@", "Engineering") |
Example: Fetching Employees by Department
let context = persistentContainer.viewContext
let fetchRequest: NSFetchRequest<Employee> = Employee.fetchRequest()
let predicate = NSPredicate(format: "department.name == %@", "Engineering")
fetchRequest.predicate = predicate
do {
let employees = try context.fetch(fetchRequest)
print("Found \(employees.count) employees")
} catch {
print("Fetch failed: \(error)")
}
Example: Case-Insensitive Search
let searchTerm = "john"
let predicate = NSPredicate(format: "name CONTAINS[cd] %@", searchTerm)
let fetchRequest: NSFetchRequest<Employee> = Employee.fetchRequest()
fetchRequest.predicate = predicate
[cd] modifier in predicates stands for case and diacritic insensitive matching.7. Sorting Fetch Results
| Property | Description | Usage |
|---|---|---|
| sortDescriptors | Array of NSSortDescriptor objects defining how to sort the fetched results | let sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)] |
Example: Sorting by Name Ascending
let request = NSFetchRequest<Employee>()
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
Example: Sorting by Multiple Fields
let sortDescriptors = [
NSSortDescriptor(key: "department", ascending: true),
NSSortDescriptor(key: "name", ascending: false)
]
request.sortDescriptors = sortDescriptors
8. Updating Managed Objects
| Property | Description | Notes |
|---|---|---|
| inserted | Indicates if the object was newly inserted into the context | NEW in iOS 17 |
| updated | Indicates if the object was modified in the context | Available since iOS 13 |
| deleted | Indicates if the object was marked for deletion | Available since iOS 13 |
Example: Updating a Managed Object
let context = persistentContainer.viewContext
let fetchRequest: NSFetchRequest<Person> = Person.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "name == %@", "John")
do {
let results = try context.fetch(fetchRequest)
if let person = results.first {
person.name = "John Smith"
try context.save()
}
} catch {
print("Failed to update person: \(error)")
}
Example: Using NSFetchedResultsController for Updates
class PersonTableViewController: UITableViewController {
lazy var fetchedResultsController: NSFetchedResultsController<Person> = {
let fetchRequest: NSFetchRequest<Person> = Person.fetchRequest()
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
let frc = NSFetchedResultsController(
fetchRequest: fetchRequest,
managedObjectContext: context,
sectionNameKeyPath: nil,
cacheName: nil
)
frc.delegate = self
return frc
}()
func updatePerson(_ person: Person) {
person.name = "Updated Name"
do {
try context.save()
} catch {
print("Save error: \(error)")
}
}
}
9. Deleting Managed Objects
| Method | Description | Notes |
|---|---|---|
| delete(_ object: NSManagedObject) | Deletes a managed object from its managed object context. | Object must be inserted into the context before deletion. |
| deleteObjects(_ objects: [NSManagedObject]) | Deletes multiple managed objects from the context. | Can be more efficient than deleting one by one. |
Example: Deleting a Single Object
let managedObjectContext = persistentContainer.viewContext
let person = NSEntityDescription.entity(forName: "Person", in: managedObjectContext)!
let personObject = NSManagedObject(entity: person, insertInto: managedObjectContext)
// Delete the object
managedObjectContext.delete(personObject)
Example: Deleting Multiple Objects
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Person")
let persons = try! managedObjectContext.fetch(fetchRequest)
managedObjectContext.deleteObjects(persons)
save() on the managed object context.
10. Saving Context Changes
| Method | Description | Notes |
|---|---|---|
| save() | Saves changes to the persistent store | Throws an error if save fails |
| hasChanges | Indicates if there are unsaved changes | Read-only property |
Example: Saving Context Changes
let context = persistentContainer.viewContext
context.insertedObjects // Check for new objects
context.updatedObjects // Check for updated objects
context.deletedObjects // Check for deleted objects
do {
try context.save()
print("Changes saved successfully")
} catch {
print("Failed to save context: \(error)")
}
save() error to prevent data loss.11. Using Batch Operations
Batch operations in Core Data allow you to perform multiple changes efficiently without individual fetch or save operations. These are particularly useful for bulk updates, deletes, or inserts.
| Operation | Description | Usage |
|---|---|---|
| NSBatchInsertRequest | Used to insert multiple objects into a Core Data store in one operation. | let insertRequest = NSBatchInsertRequest(entity: entity, values: [ ... ]) |
| NSBatchUpdateRequest | Updates all matching objects in the store with new values. | let updateRequest = NSBatchUpdateRequest(entity: entity, propertiesToUpdate: [ ... ]) |
| NSBatchDeleteRequest | Deletes all matching objects from the store. | let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest) |
Example: Batch Insert
let context = persistentContainer.newBackgroundContext()
let entity = NSEntityDescription.entity(forName: "Person", in: context)
let values = [
["name": "Alice", "age": 30],
["name": "Bob", "age": 25]
]
let insertRequest = NSBatchInsertRequest(entity: entity!, values: values)
try context.execute(insertRequest)
Example: Batch Update
let context = persistentContainer.newBackgroundContext()
let entity = NSEntityDescription.entity(forName: "Person", in: context)
let updateRequest = NSBatchUpdateRequest(entity: entity!, propertiesToUpdate: ["age": 35])
try context.execute(updateRequest)
Example: Batch Delete
let context = persistentContainer.newBackgroundContext()
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Person")
fetchRequest.predicate = NSPredicate(format: "age < 20")
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
try context.execute(deleteRequest)
12. Implementing Background Contexts
Background contexts are used to perform Core Data operations off the main thread, improving app responsiveness. They are typically created with a private queue concurrency type.
| Property | Description | Value |
|---|---|---|
| concurrencyType | Queue type for the context | NSPrivateQueueConcurrencyType |
| parent | Parent context (optional) | NSManagedObjectContext |