Skip to content

Instantly share code, notes, and snippets.

@sixten
Created October 5, 2012 16:20
Show Gist options
  • Save sixten/3840794 to your computer and use it in GitHub Desktop.
Save sixten/3840794 to your computer and use it in GitHub Desktop.
RKManagedObjectStore subclass that is configuration-aware
//
// RDManagedObjectStore.h
// Capri
//
// Created by Sixten Otto on 4/17/12.
// Copyright (c) 2012 Results Direct. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
#import <RestKit/CoreData.h>
@interface RDManagedObjectStore : RKManagedObjectStore
+ (RDManagedObjectStore*)objectStoreWithStoreFilename:(NSString *)storeFilename inDirectory:(NSString *)directory usingSeedDatabaseName:(NSString *)nilOrNameOfSeedDatabaseInMainBundle managedObjectModel:(NSManagedObjectModel*)nilOrManagedObjectModel configuration:(NSString *)nilOrModelConfiguration;
- (NSError *)addPersistentStoreForConfiguration:(NSString *)configuration withFilename:(NSString *)storeFilename inDirectory:(NSString *)directory;
@end
//
// RDManagedObjectStore.m
// Capri
//
// Created by Sixten Otto on 4/17/12.
// Copyright (c) 2012 Results Direct. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
#import "RDManagedObjectStore.h"
#import <RestKit/RKDirectory.h>
#import <RestKit/RKSearchWordObserver.h>
@interface RKManagedObjectStore (Private)
- (void)createPersistentStoreCoordinator;
- (void)createStoreIfNecessaryUsingSeedDatabase:(NSString*)seedDatabase;
@end
@interface RDManagedObjectStore ()
@property (nonatomic, strong, readwrite) NSString *storeFilename;
@property (nonatomic, strong, readwrite) NSString *pathToStoreFile;
@property (nonatomic, strong, readwrite) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, strong, readwrite) NSPersistentStoreCoordinator *persistentStoreCoordinator;
@property (nonatomic, strong, readwrite) NSManagedObjectContext *primaryManagedObjectContext;
@property (copy, nonatomic) NSString *configuration;
- (id)initWithStoreFilename:(NSString *)storeFilename inDirectory:(NSString *)nilOrDirectoryPath usingSeedDatabaseName:(NSString *)nilOrNameOfSeedDatabaseInMainBundle managedObjectModel:(NSManagedObjectModel*)nilOrManagedObjectModel configuration:(NSString *)nilOrModelConfiguration;
- (NSError *)addPersistentStoreForConfiguration:(NSString *)configuration URL:(NSURL *)storeURL;
@end
@implementation RDManagedObjectStore
@synthesize storeFilename = rd_storeFilename;
@synthesize pathToStoreFile = rd_pathToStoreFile;
@synthesize managedObjectModel = rd_managedObjectModel;
@synthesize persistentStoreCoordinator = rd_persistentStoreCoordinator;
@synthesize primaryManagedObjectContext = rd_primaryManagedObjectContext;
@synthesize configuration = rd_configuration;
+ (RDManagedObjectStore*)objectStoreWithStoreFilename:(NSString *)storeFilename inDirectory:(NSString *)directory usingSeedDatabaseName:(NSString *)nilOrNameOfSeedDatabaseInMainBundle managedObjectModel:(NSManagedObjectModel*)nilOrManagedObjectModel configuration:(NSString *)nilOrModelConfiguration
{
return [[self alloc] initWithStoreFilename:storeFilename inDirectory:directory usingSeedDatabaseName:nilOrNameOfSeedDatabaseInMainBundle managedObjectModel:nilOrManagedObjectModel configuration:nilOrModelConfiguration];
}
- (id)initWithStoreFilename:(NSString *)storeFilename inDirectory:(NSString *)nilOrDirectoryPath usingSeedDatabaseName:(NSString *)nilOrNameOfSeedDatabaseInMainBundle managedObjectModel:(NSManagedObjectModel*)nilOrManagedObjectModel configuration:(NSString *)nilOrModelConfiguration {
self = [self init];
if (self) {
self.storeFilename = storeFilename;
self.configuration = nilOrModelConfiguration;
if( nilOrDirectoryPath == nil ) {
nilOrDirectoryPath = [RKDirectory applicationDataDirectory];
[RKDirectory ensureDirectoryExistsAtPath:nilOrDirectoryPath error:nil];
}
else {
BOOL isDir;
NSAssert1([[NSFileManager defaultManager] fileExistsAtPath:nilOrDirectoryPath isDirectory:&isDir] && isDir == YES, @"Specified storage directory exists", nilOrDirectoryPath);
}
self.pathToStoreFile = [nilOrDirectoryPath stringByAppendingPathComponent:storeFilename];
if (nilOrManagedObjectModel == nil) {
nilOrManagedObjectModel = [NSManagedObjectModel mergedModelFromBundles:[NSBundle allBundles]];
}
NSMutableArray *allManagedObjectModels = [NSMutableArray arrayWithObject:nilOrManagedObjectModel];
self.managedObjectModel = [NSManagedObjectModel modelByMergingModels:allManagedObjectModels];
if (nilOrNameOfSeedDatabaseInMainBundle) {
[self createStoreIfNecessaryUsingSeedDatabase:nilOrNameOfSeedDatabaseInMainBundle];
}
[self createPersistentStoreCoordinator];
self.primaryManagedObjectContext = [self newManagedObjectContext];
self.cacheStrategy = [RKInMemoryManagedObjectCache new];
// Ensure there is a search word observer
[RKSearchWordObserver sharedObserver];
// Hydrate the defaultObjectStore
if (! [[self class] defaultObjectStore] ) {
[[self class] setDefaultObjectStore:self];
}
}
return self;
}
- (NSError *)addPersistentStoreForConfiguration:(NSString *)configuration URL:(NSURL *)storeURL
{
NSPersistentStore* store = nil;
NSError *error = nil;
/* There seems to be trouble with combining configurations and migration. So
* do this in two steps: first, attach the store with NO configuration,
* but WITH migration options; then remove it and reattach WITH configuration,
* but NOT migration options.
*
* http://blog.atwam.com/blog/2012/05/11/multiple-persistent-stores-and-seed-data-with-core-data/
* http://stackoverflow.com/questions/1774359/core-data-migration-error-message-model-does-not-contain-configuration-xyz
*
*/
// migration pass
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,
nil];
store = [self.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error];
if( nil == store ) {
return error;
}
// remove store
if( ![self.persistentStoreCoordinator removePersistentStore:store error:&error] ) {
return error;
}
// reattach store
store = [self.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:configuration URL:storeURL options:nil error:&error];
if( nil == store ) {
return error;
}
return nil;
}
- (NSError *)addPersistentStoreForConfiguration:(NSString *)configuration withFilename:(NSString *)storeFilename inDirectory:(NSString *)directory
{
NSURL* storeURL = [[NSURL fileURLWithPath:directory] URLByAppendingPathComponent:storeFilename];
return [self addPersistentStoreForConfiguration:configuration URL:storeURL];
}
- (void)createPersistentStoreCoordinator {
NSURL *storeUrl = [NSURL fileURLWithPath:self.pathToStoreFile];
self.persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];
NSError* error = [self addPersistentStoreForConfiguration:self.configuration URL:storeUrl];
if( error ) {
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(managedObjectStore:didFailToCreatePersistentStoreCoordinatorWithError:)]) {
[self.delegate managedObjectStore:self didFailToCreatePersistentStoreCoordinatorWithError:error];
}
else {
NSAssert(NO, @"Managed object store failed to create persistent store coordinator: %@", error);
}
}
}
- (NSManagedObjectContext *)newManagedObjectContext
{
NSManagedObjectContext *managedObjectContext = nil;
if( [NSThread isMainThread] ) {
managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
}
else {
managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
}
[managedObjectContext setPersistentStoreCoordinator:self.persistentStoreCoordinator];
[managedObjectContext setUndoManager:nil];
[managedObjectContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
managedObjectContext.managedObjectStore = self;
return managedObjectContext;
}
@end
// set up object store
RDManagedObjectStore* store = [RDManagedObjectStore
objectStoreWithStoreFilename:@"Server.sqlite"
inDirectory:appSupportPath
usingSeedDatabaseName:[self serverSeedDatabaseName]
managedObjectModel:managedObjectModel
configuration:@"Server"];
store.delegate = self;
// don't back up the server-sourced content
[self setDontBackupOnURL:[[store.persistentStoreCoordinator.persistentStores lastObject] URL]];
// separate storage for user-generated content
[store addPersistentStoreForConfiguration:@"UserGenerated" withFilename:@"UserGenerated.sqlite" inDirectory:appSupportPath];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment