What is Core Data/Core Data & Swift Best Practices Part 1
Core Data. Mention it in a room of developers and brace for a symphony of reactions. Nevertheless, mastering this beast is a rite of passage for any serious iOS developer. This series aims to: (1) Help you understand Core Data's architecture, (2) Illuminate what happens under Core Data's hood, and (3) Provide a sample app in Swift to kickstart your journey with Core Data, showcasing best practices.
Firstly, remember this: Core Data is a framework. It's an object graph management and persistence framework that enables you to manipulate data as objects, regardless of how they're stored on disk. It abstracts away the complexities of direct data storage, which is a huge boon in the object-oriented programming (OOP) world.
Core Data resides between your application and a persistent store, a fancy term for data that survives hardware resets. This could be a SQLite database, XML file (though not usable as a persistent store in iOS), binary store, or an in-memory store.
In its simplest form, the Core Data stack contains four components:
Managed Object (NSManagedObject)
Managed Object Context (NSManagedObjectContext)
Persistent Store (NSPersistentStore)
Persistent Store Coordinator (NSPersistentStoreCoordinator)
However, starting from iOS 10, Apple introduced a new class, NSPersistentContainer, that encapsulates the Core Data stack, making it easier to manage.
// Core Data Stack setup with NSPersistentContainer
let container = NSPersistentContainer(name: "Model")
container.loadPersistentStores { (storeDescription, error) in
if let error = error as NSError? {
// Handle error
fatalError("Unresolved error \(error), \(error.userInfo)")
}
}
Managed Objects, created by Core Data, exist in a specific context - the Managed Object Context. The context tracks changes made to its managed objects such as insertions, deletions, and updates. Each managed object is aware of its context. Core Data supports multiple contexts, but for simplicity, we'll stick to one in this series.
// Creating a new Managed Object
let context = container.viewContext
let newObject = NSEntityDescription.insertNewObject(forEntityName: "Entity", into: context)
The context connects to a persistent store coordinator, which mediates between the persistent store and the managed object context. You can use a combination of multiple persistent stores and store coordinators.
Here's a visual overview of Core Data's architecture:
src: objc.io
Core Data simplifies object relationships. One to many or many to many, you're just manipulating sets. If you had to directly interact with SQLite, you'd have to create a lookup table to relate your objects, which can be a headache to maintain.
Core Data also presents some challenges when working across multiple threads. The golden rule is: one context per thread, and never share Managed Objects between threads. We'll delve deeper into this topic in the next parts of the series.
That's it for now. I believe in understanding the underlying concepts before diving into the code. In Part 2, we'll start coding our sample app, taking advantage of Swift's Combine and SwiftUI for a modern, reactive approach to iOS development, and we'll compare Core Data with other options like Realm and SQLite to give you a broader perspective. We'll also tackle best practices, common pitfalls, and how to handle data model versioning and error handling effectively. Stay tuned!