Create a Single Project
name it “CoreDataTut”
We’re going to do everything by scratch so do not check the Core Data box. Leave it blank.
Generate a xcdatamodeld file
Once the project is generated, look at your list view on the left side. We need a .xcdatamodeld file, which represents the model diagram of our entities. The reason why is because core data code need to use a .xcdatamodeld to initialize a Managed object Model, which it uses to save state and data.
See how to create a data model here
In our core data stack, we actually load this file into an NSManagedObjectModel object.
Make sure that the name of your data model . xcdatamodeld file matches that of the name you use in your code. We do this by using global #define as noted in the code below.
The core data stack
First we make a singleton object out of Core data’s basics by creating a singleton class that houses
- NSManagedObjectContext
- NSManagedObjectModel
- NSPersistentStoreCoordinator
From a picture perspective, core data’s basic parts are connected like this:
Create the source file:
Click on Cocoa Touch Class, and input “THCoreDataStack”. The subclass should be “NSObject”.
THCoreDataStack.h source
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#import <Foundation/Foundation.h> #import <CoreData/CoreData.h> @interface THCoreDataStack : NSObject { } +(instancetype) defaultStack; //instantiations of self MUST accesses this class method. //no other instantiations of this method is possible. Thus we ensure 1 entry point. //public @property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext; @property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel; @property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator; - (void)saveContext; - (NSURL *)applicationDocumentsDirectory; -(void)hadoken; @end |
THCoreDataStack.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
#import <CoreData/CoreData.h> #import "THCoreDataStack.h" #define SQL_FILENAME @"Model.sqlite" #define MOMD_NAME @"Model" @implementation THCoreDataStack @synthesize managedObjectContext = _managedObjectContext; @synthesize managedObjectModel = _managedObjectModel; @synthesize persistentStoreCoordinator = _persistentStoreCoordinator; -(void)hadoken { NSLog(@"HA DOOOO KEN! address of defaultStack - %p", self); } +(instancetype)defaultStack { //all instantiations of THCoreDataStack, will access this method //we use this one and only statick variable static THCoreDataStack * defaultStack; NSLog(@"address of defaultStack - %p", defaultStack); if(!defaultStack) { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //here, code gets executed once //hence we alloc our one and only defaultStack variable once defaultStack = [[self alloc] init]; NSLog(@"ALLOC defaultStack on GCD - %p", defaultStack); }); } return defaultStack; } - (NSURL *)applicationDocumentsDirectory { return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; } |
Managed Object Model
The lazy loading and getter method of our MOM.
http://stackoverflow.com/questions/10579767/why-the-extension-is-momd-but-not-xcdatamodel-when-search-the-path-for-the-m
The mom and momd files are compiled versions of xcdatamodel and xcdatamodeld files…so here we’re just saying that we need to allocate a NSManagedObjectModel object and init with the compiled versions of our xcdatamodeld files.
1 2 3 4 5 6 7 8 9 |
- (NSManagedObjectModel *)managedObjectModel { // The managed object model for the application. It is a fatal error for the application not to be able to find and load its model. if (_managedObjectModel != nil) { return _managedObjectModel; } NSURL *modelURL = [[NSBundle mainBundle] URLForResource:MOMD_NAME withExtension:@"momd"]; _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; return _managedObjectModel; } |
Basic lazy loading of the PSC, and its getter. When creating the PSC, we use a #define macro for the sql’s file name.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator { // The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. if (_persistentStoreCoordinator != nil) { return _persistentStoreCoordinator; } // Create the coordinator and store _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:SQL_FILENAME]; NSError *error = nil; NSString *failureReason = @"There was an error creating or loading the application's saved data."; if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) { // Report any error we got. NSMutableDictionary *dict = [NSMutableDictionary dictionary]; dict[NSLocalizedDescriptionKey] = @"Failed to initialize the application's saved data"; dict[NSLocalizedFailureReasonErrorKey] = failureReason; dict[NSUnderlyingErrorKey] = error; error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict]; // Replace this with code to handle the error appropriately. // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } return _persistentStoreCoordinator; } |
Managed Object Context – scratch pad for DB changes
This MOC is basically a scratch pad for database changes. Later, when you save context, it will be reflected to the Persistent Store Coordinator, and thus, the database.
We have basic lazy loading and get methods for the MOC.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
- (NSManagedObjectContext *)managedObjectContext { // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) if (_managedObjectContext != nil) { return _managedObjectContext; } NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; if (!coordinator) { return nil; } _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType: NSPrivateQueueConcurrencyType]; [_managedObjectContext setPersistentStoreCoordinator:coordinator]; return _managedObjectContext; } |
Saving the Context
This method is called right after an Object is returned from the MOC designated to be inserted or changed.
That way the MOC’s changes are safely saved.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#pragma mark - Core Data Saving support - (void)saveContext { NSManagedObjectContext *managedObjectContext = self.managedObjectContext; if (managedObjectContext != nil) { NSError *error = nil; if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) { // Replace this implementation with code to handle the error appropriately. // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } } } @end |