iOS Recipes - Matt Drance [80]
configuration:nil
URL:storeURL
options:options
error:&error]) {
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:error
forKey:NSUnderlyingErrorKey];
NSException *exc = nil;
NSString *reason = @"Could not create persistent store.";
exc = [NSException exceptionWithName:NSInternalInconsistencyException
reason:reason
userInfo:userInfo];
@throw exc;
}
persistentStoreCoordinator = psc;
Working with NSError
Per Apple’s NSPersistentStore reference and general Cocoa convention, NSError arguments passed to a specific API should not be inspected directly unless the API returns a value indicating an error state—often nil or NO. Failing to heed this guidance can lead to subtle but serious bugs in your code.
The code also supports “preinstallation” of an existing database shipped with the application if one does not already exist. We do this before creating the model’s persistent store so we can present the user with some placeholder data in our app on a first-time launch. This part is optional, and there’s no need to disable the code. If you don’t want a placeholder, don’t supply a preinstalled database file.
BasicDataModel/Shared/PRPBasicDataModel.m
NSString *pathToLocalStore = [self pathToLocalStore];
NSString *pathToDefaultStore = [self pathToDefaultStore];
NSError *error = nil;
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL noLocalDBExists = ![fileManager fileExistsAtPath:pathToLocalStore];
BOOL defaultDBExists = [fileManager fileExistsAtPath:pathToDefaultStore];
if (noLocalDBExists && defaultDBExists) {
if (![[NSFileManager defaultManager] copyItemAtPath:pathToDefaultStore
toPath:pathToLocalStore
error:&error]) {
NSLog(@"Error copying default DB to %@ (%@)",
pathToLocalStore, error);
}
}
Locations for the various files behind this data model—the model file, the working database, and the preinstalled “default” database—are abstracted into accessor methods, which you can edit or override to customize the paths.
‑storeFileName returns the name of the SQLite database, named similarly to the model (.momd) file: if the model is BasicDataModel.momd, then the store file is BasicDataModel.sqlite.
‑pathToLocalStore returns a path to the active database in the app’s sandbox. It defaults to ~/Documents/ ‑pathToDefaultStore returns the path to a default database in the app bundle for preinstallation. BasicDataModel/Shared/PRPBasicDataModel.m - (NSString *)storeFileName { return [[self modelName] stringByAppendingPathExtension:@"sqlite"]; } - (NSString *)pathToLocalStore { NSString *storeName = [self storeFileName]; NSString *docPath = [self documentsDirectory]; return [docPath stringByAppendingPathComponent:storeName]; } - (NSString *)pathToDefaultStore { NSString *storeName = [self storeFileName]; return [[NSBundle mainBundle] pathForResource:storeName ofType:nil]; } We can instantiate a BasicDataModel in code or in Interface Builder where it is easily recognized and connected to the app delegate’s dataModel property. Figure 38. Initializing the model from Interface Builder * * * The NSManagedObjectContext class is the main interface to most Core Data operations. Our basic data model lazily creates a single “main” context for all of its queries. Since this is a basic model, it doesn’t bother with multiple context or thread support. If you need to use multiple contexts, you can easily modify the class to generate fresh ones or just create them on the fly using the main context’s persistent store coordinator. BasicDataModel/Shared/PRPBasicDataModel.m - (NSManagedObjectContext *)mainContext { if (mainContext == nil) { mainContext = [[NSManagedObjectContext alloc] init]; NSPersistentStoreCoordinator *psc = self.persistentStoreCoordinator; [mainContext setPersistentStoreCoordinator:psc]; } return mainContext; } The BasicDataModel project accompanying this recipe creates the model and connects it to the app delegate from Interface Builder, as seen in Figure 38,