Core Data
-
date_range Nov. 20, 2016 - Sunday infosortiOS Developmentlabelioscore dataiclouduimanageddocument
“It’s all about how core data works.”
Database
Some times you need to store large amount of data or query it in a sophisticated manner. And we want it to be object-oriented.
Core data framework is very powerful.
How does it work?
- Create a visual mapping between database and objects;
- Create and query for objects using object-oriented API;
- Access the “columns in the database table” using @propertys on those objects.
- Create a core data model, the visual map in xcode.
An Object Context
You need an NSManagedObjectContext.
It is the hub around which all Core Data activity turns.
How do I get one?
There are two ways:
-
Create a
UIMangedDocumentand ask for itsmanagedObjectContext(a@property). -
Click the “Use Core Data” button when you create a project (only works with certain templates, then you AppDelegate will have a managedObjectContext @property).
There will be an
@propertyand a methodsaveContextgenerated for managing the core data.
@property (readonly, strong) NSPersistentContainer *persistentContainer;
// The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.
- (void)saveContext;
We’re going to focus on doing the first one UIManagedDocument.
UIManagedDocument
UIManagedDocument inherits from UIDocument which provides a lot of mechanism for the management of storage. It also good for iCloud support. Think of a UIManagedDocument as simply a container for your Core Data database.
1. Create a UIManagedDocument
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *documentsDirectory = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] firstObject];
NSString *documentName = @"MyDocument";
NSURL *url = [documentsDirectory URLByAppendingPathComponent:documentName];
UIManagedDocument *document = [[UIManagedDocument alloc] initWithFileURL:url];
This creates the UIManagedDocument instance, but does not open nor create the underlying file.
2. Open or Create a UIManagedDocument
Check if the UIManagedDocument’s underlying file exists on disk:
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:[url path]];
Here is an example:
self.document = [[UIManagedDocument alloc] initWithFileURL:(URL *)url];
if ([[NSFileManager defaultManager] fileExistsAtPath:[url path]]) {
[document openWithCompletionHandler:^(BOOL success) {
if (success) [self documentIsReady];
if (!success) NSLog(@"couldn’t open document at %@", url);
}];
} else {
[document saveToURL:url forSaveOperation:UIDocumentSaveForCreating
completionHandler:^(BOOL success) {
if (success) [self documentIsReady];
if (!success) NSLog(@"couldn't create document at %@", url);
}];
}
- (void)documentIsReady {
if (self.document.documentState == UIDocumentStateNormal) {
// start using document
NSManagedObjectContext *context = self.document.managedObjectContext;
}
}
3. Saving & Closing
- Notice that
UIManagedDocuments AUTOSAVE themselves! - Will automatically close if there are no strong pointers left to it.
NSNotification
How would you watch a document’s managedObjectContext?
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[center addObserver:self
selector:@selector(contextChanged:)
name:NSManagedObjectContextDidSaveNotification
object:document.managedObjectContext]; //don’t pass nil here!
}
- (void)viewWillDisappear:(BOOL)animated
{
[center removeObserver:self
name:NSManagedObjectContextDidSaveNotification
object:document.managedObjectContext];
[super viewWillDisappear:animated];
}
Core Data
Now we use the UIManagedDocument’s managedObjectContext @property to insert/delete objects in the database and query for objects in the database.
Query
We can do this by executing an NSFetchRequest in our NSManagedObjectContext.
Four important things involved in creating an NSFetchRequest
Entityto fetch (required);- How many objects to fetch at a time and/or maximum to fetch (optional, default: all);
NSSortDescriptorto specify the order in which the array of fetched objects are returned;NSPredicate* specifying which of those Entities of those Entities to fetch (optional, default is all of them).
Dependency Injection
Q: How to get managedObjectContext for viewController other than getting it from appDelegate?
A: Use Dependency Injection
Basically the caller/constructor should be setting the NSManagedObjectContext onto the called/constructed.
In your AppDelegate you should set the NSManagedObjectContext into the rootViewController that is associated with the UIWindow.
Your rootViewController should then set the NSManagedObjectContext into the next view controller and so on.
How? It is just a simple proper on the view controller class and the caller uses:
[nextViewController setManagedObjectContext:[self managedObjectContext]];
Some others may recommend a singleton but that is another deep dark pit that is best avoided.
KF
