What is Core Data?
Core Data is an object-graph management system that also provides data persistence. It is not simply a persistence framework. Its features can be used without even persisting data. However, usually, it interfaces with a SQLite database (and this will be my assumption for most of this article).
Notable Features:
- Change tracking and undo support.
- Relationship maintenance.
- Faults (Lazy loading).
- Automatic property validation.
- Easy integration with UITableView and UICollectionView.
- Automatic support for storing objects in external data repositories.
What Core Data is Not
- Core Data is not a relational database or a relational database management system.
- It can use SQLite as one of its persistent store types, but it is not in and of itself a database. You could set up Core Data to just use an in-memory store just to get the change tracking and management features without persistence.
- Core Data is not a silver bullet.
- It does a lot behind the scenes to help make your application more efficient, but don’t assume that your app will be efficient just because it’s using Core Data. It still takes work to fully take advantage of the efficiency that Core Data can provide.
The Core Data Stack
Below is a very basic example of a Core Data stack. Each component is briefly described below starting from the bottom of the stack.
Store File
- This is the actual file on disk that is used to store the data. Usually, this will be a SQLite database.
- This is actually an optional component of the Core Data stack. If an in-memory store is chosen, no data will be stored in a file on disk.
Persistent Object Store
- Object of type NSPersistentStore (Abstract base class).
- This is the object that represents the store. You will likely only use this when you are setting up your stack.
- This object is initialized with a type (e.g. NSSQLiteStoreType), a URL to the file on disk, as well as some other configuration options.
Persistent Store Coordinator
- Object of type NSPersistentStoreCoordinator.
- The coordinator is designed to be a facade for the persistent store(s).
- Can only be associated with one managed object model.
Managed Object Model
- Object of type NSManagedObjectModel.
- The managed object model is essentially the database schema. It defines what objects there are, what properties those objects have, and how the different kinds of objects relate to each other.
- Can be created graphically using Xcode’s Data Model Design tool.
Managed Object Context
- Object of type NSManagedObjectContext.
- This is the object that you work with directly (and frequently) to interact with model objects.
- The managed object context (MOC) is essentially an in-memory sandbox of model objects. You can create, fetch, modify, or delete objects in the MOC, but the changes won’t be reflected in the persistent store until the MOC is saved.
- You can have more than one MOC, but you should only use a MOC on a single thread (by default, the thread that created it). Core data uses thread (or serialized queue) confinement to protect the integrity of MOCs and their objects.
Model Objects
- Objects that are subclasses of NSManagedObject.
- Model objects are defined by the Managed Object Model. They are subclasses of NSManagedObject which is a generic class that implements all the basic behavior required of a Core Data model object.
- Each NSManagedObject is an object representation of a row in the database.
- Each NSManagedObject is associated with ONE Managed Object Context – the context that created it or retrieved it. It should not be passed around between contexts.
Designing a Model
Designing the object model for your application is one of the most important steps in developing a quality application. If you spend some extra time designing your model, you will save yourself development time in the future. Your design can also have a large impact on the level of efficiency you are able to achieve. Unlike the more pure modeling principles used to design a database schema for a web service, your model design may partially reflect the needs of your UI in order to provide more efficient queries.
Below are screenshots taken from Xcode’s Data Model Design tool. The show a very basic model for a recipe application.
Entities
An entity represents a table in your database. It is the blueprint for the NSManagedObject subclass instances that you will create and use throughout your application.
Attributes
Attributes are properties of your object. They translate to a column in your database table and a property in your managed object. You can choose from a variety of primitive values that the database has to offer such as a string, integer, or date.
Relationships
A relationship describes how one entity relates to another. Two important aspects of this are the cardinality and the deletion rule.
- Cardinality – The multiplicity of each end of the relationship.
- One-to-many – Lets say that each Department has a group of Employees that can only work for a single Department. This would be a “one-to-many” relationship since each Department could have many Employees and each Employee can only work for one Department.
- Many-to-many – If a single Employee could work for multiple Departments, then our Department/Employee relationship would be “many-to-many” because each Department could have many Employees and each Employee could work for multiple Departments.
- We chose a cardinality of many-to-many for our Recipe/Ingredient relationship above. A Recipe for scrambled eggs could have multiple Ingredients–for example: Eggs, Milk, and Cheese–but these Ingredients could also be used in other recipes. So, we define this with a many-to-many relationship.
- Deletion Rule – The action to take on the objects at the destination of a relationship when the source object is deleted.
- Deny – If there is at least one object at the relationship destination, then the source object cannot be deleted. For example, if you want to remove a department, you must ensure that all the employees in that department are first transferred elsewhere (or fired!) otherwise the department cannot be deleted.
- Cascade – Delete the objects at the destination of the relationship. For example, if you delete a department, fire all the employees in that department at the same time.
- Nullify – Set the inverse relationship for objects at the destination to null. For example, if you delete a department, set the department for all the current members to null. This only makes sense if the department relationship for an employee is optional, or if you ensure that you set a new department for each of the employees before the next save operation.
- No Action – Do nothing to the object at the destination of the relationship. For example, if you delete a department, leave all the employees as they are, even if they still believe they belong to that department.
WARNING!
If you use the No Action rule, it is up to you to ensure that the consistency of the object graph is maintained. You are responsible for setting any inverse relationship to a meaningful value. This may be of benefit in a situation where you have a to-many relationship and there may be a large number of objects at the destination.
Working With Model Objects and MOCs
C
reating
[NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([Recipe class]) inManagedObjectContext:context];
Fetching
NSEntityDescription *entity = [NSEntityDescription entityForName:NSStringFromClass([Recipe class]) inManagedObjectContext:context];NSSortDescriptor *dateSorter = [[NSSortDescriptor alloc] initWithKey:@"dateUpdated" ascending:NO];NSPredicate *predicate = [NSPredicate predicateWithFormat:@"description CONTAINS %@", usersSearchString]; // Careful, CONTAINS is not a cheap DB operationNSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];fetchRequest.entity = entity;fetchRequest.sortDescriptors = @[dateSorter];NSError *e;NSArray *results = [context executeFetchRequest:fetchRequest error:&e];if (e){ // handle error...}
Deleting
[context deleteObject:recipe];
Saving
NSError *e;[context save:&e];if (e){ // handle error...}