Skip to content

Instantly share code, notes, and snippets.

@jamiepinkham
Created January 30, 2014 04:54
Show Gist options
  • Save jamiepinkham/8702841 to your computer and use it in GitHub Desktop.
Save jamiepinkham/8702841 to your computer and use it in GitHub Desktop.
//
// UBCoreDataStack.h
// LevelDBMigrator
//
// Created by Jamie Pinkham on 1/28/14.
// Copyright (c) 2014 Jamie Pinkham. All rights reserved.
//
#import <Foundation/Foundation.h>
@class UBCoreDataController;
@protocol UBCoreDataControllerDelegate <NSObject>
@optional
- (BOOL)retryAfterPersistentStoreCreationFailedWithError:(NSError *)error coreDataController:(UBCoreDataController *)stack;
@end
@interface UBCoreDataController : NSObject
+ (instancetype)defaultController;
- (instancetype)initWithStoreFilename:(NSString *)filename;
- (instancetype)initWithStoreFilename:(NSString *)filename migratesAutomatically:(BOOL)migratesAutomatically;
- (instancetype)initWithStoreFilename:(NSString *)filename migratesAutomatically:(BOOL)migratesAutomatically modelName:(NSString *)modelName;
- (instancetype)initWithStoreFilename:(NSString *)filename migratesAutomatically:(BOOL)migratesAutomatically modelName:(NSString *)modelName storeType:(NSString *)storeType;
- (instancetype)initWithStoreFilename:(NSString *)filename modelName:(NSString *)modelName storeType:(NSString *)storeType storeOptions:(NSDictionary *)storeOptions;
- (instancetype)initWithStoreURL:(NSURL *)storeURL storeType:(NSString *)storeType storeOptions:(NSDictionary *)storeOptions modelConfiguration:(NSString *)modelConfiguration modelURL:(NSURL *)modelURL;
@property (nonatomic, readonly) NSManagedObjectContext *mainQueueManagedObjectContext;
@property (nonatomic, readonly) NSManagedObjectContext *privateQueueManagedObjectContext;
- (NSManagedObjectContext *)childContextWithConcurrencyType:(NSManagedObjectContextConcurrencyType)type;
@property (nonatomic, readonly) NSURL *storeURL;
@property (nonatomic, readonly) NSString *storeType;
@property (nonatomic, readonly) NSDictionary *storeOptions;
@property (nonatomic, readonly) NSString *modelConfiguration;
@property (nonatomic, readonly) NSURL *modelURL;
@property (nonatomic, weak) id<UBCoreDataControllerDelegate> delegate;
@end
//
// UBCoreDataStack.m
// LevelDBMigrator
//
// Created by Jamie Pinkham on 1/28/14.
// Copyright (c) 2014 Jamie Pinkham. All rights reserved.
//
#import "UBCoreDataController.h"
#import <CoreData/CoreData.h>
NSString * UB_defaultDatabaseFilename(void)
{
NSBundle *mainBundle = [NSBundle mainBundle];
NSString *appName = [mainBundle objectForInfoDictionaryKey:(__bridge NSString *)kCFBundleIdentifierKey];
NSString *filename = [NSString stringWithFormat:@"%@.sqlite", appName];
return filename;
}
NSURL * UB_databaseFileWithFilename(NSString *databaseFileName)
{
NSError *findApplicationSupportDirectoryError = nil;
NSURL *applicationSupportDirectory = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:&findApplicationSupportDirectoryError];
if (applicationSupportDirectory == nil)
{
NSLog(@"Error locating the Application Support directory. %@", findApplicationSupportDirectoryError);
return nil;
}
return [applicationSupportDirectory URLByAppendingPathComponent:databaseFileName];
}
NSURL * UB_modelURLWithModelName(NSString *modelName)
{
if(modelName)
{
NSBundle *mainBundle = [NSBundle mainBundle];
NSURL *url = [mainBundle URLForResource:modelName withExtension:@"momd"];
return url;
}
else
{
return nil;
}
}
@interface UBCoreDataController ()
@property (nonatomic, strong) NSPersistentStoreCoordinator *mainQueuePersistentStoreCoordinator;
@property (nonatomic, strong) NSPersistentStoreCoordinator *privateQueuePersistentStoreCoordinator;
@property (nonatomic, strong) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, strong) NSManagedObjectContext *privateQueueManagedObjectContext;
@property (nonatomic, strong, readwrite) NSManagedObjectContext *mainQueueManagedObjectContext;
@end
@implementation UBCoreDataController
+ (instancetype)defaultController
{
static UBCoreDataController *defaultController = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
defaultController = [[[self class] alloc] initWithStoreFilename:UB_defaultDatabaseFilename() migratesAutomatically:YES];
});
return defaultController;
}
- (id)init
{
return [self initWithStoreFilename:UB_defaultDatabaseFilename()];
}
- (instancetype) initWithStoreFilename:(NSString *)filename
{
return [self initWithStoreFilename:filename migratesAutomatically:NO];
}
- (instancetype)initWithStoreFilename:(NSString *)filename migratesAutomatically:(BOOL)migratesAutomatically
{
return [self initWithStoreFilename:filename migratesAutomatically:migratesAutomatically modelName:nil];
}
- (instancetype)initWithStoreFilename:(NSString *)filename migratesAutomatically:(BOOL)migratesAutomatically modelName:(NSString *)modelName
{
return [self initWithStoreFilename:filename migratesAutomatically:migratesAutomatically modelName:modelName storeType:nil];
}
- (instancetype)initWithStoreFilename:(NSString *)filename migratesAutomatically:(BOOL)migratesAutomatically modelName:(NSString *)modelName storeType:(NSString *)storeType
{
NSDictionary *storeOptions = @{
NSMigratePersistentStoresAutomaticallyOption : @(migratesAutomatically),
NSInferMappingModelAutomaticallyOption : @(migratesAutomatically)
};
return [self initWithStoreFilename:filename modelName:modelName storeType:storeType storeOptions:storeOptions];
}
- (instancetype)initWithStoreFilename:(NSString *)filename modelName:(NSString *)modelName storeType:(NSString *)storeType storeOptions:(NSDictionary *)storeOptions;
{
NSURL *storeURL = UB_databaseFileWithFilename(filename);
NSURL *modelURL = UB_modelURLWithModelName(modelName);
return [self initWithStoreURL:storeURL storeType:storeType storeOptions:storeOptions modelConfiguration:nil modelURL:modelURL];
}
- (instancetype)initWithStoreURL:(NSURL *)storeURL storeType:(NSString *)storeType storeOptions:(NSDictionary *)storeOptions modelConfiguration:(NSString *)modelConfiguration modelURL:(NSURL *)modelURL
{
if(self = [super init])
{
_storeURL = [storeURL copy];
_storeType = [storeType copy];
if(_storeType == nil)
{
_storeType = NSSQLiteStoreType;
}
_storeOptions = [storeOptions copy];
_modelURL = [modelURL copy];
_modelConfiguration = [modelConfiguration copy];
UIApplication *application = [UIApplication sharedApplication];
NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
[defaultCenter addObserver:self selector:@selector(handleDidEnterBackgroundNotification:) name:UIApplicationDidEnterBackgroundNotification object:application];
[defaultCenter addObserver:self selector:@selector(handleManagedObjectContextDidSaveNotification:) name:NSManagedObjectContextDidSaveNotification object:nil];
}
return self;
}
- (void)dealloc
{
UIApplication *application = [UIApplication sharedApplication];
NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
[defaultCenter removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:application];
[defaultCenter removeObserver:self name:NSManagedObjectContextDidSaveNotification object:nil];
}
- (NSManagedObjectModel *)managedObjectModel
{
if(_managedObjectModel == nil)
{
if(self.modelURL)
{
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:self.modelURL];
}
else
{
NSBundle *mainBundle = [NSBundle mainBundle];
_managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:@[mainBundle]];
}
}
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)mainQueuePersistentStoreCoordinator
{
if(_mainQueuePersistentStoreCoordinator == nil)
{
_mainQueuePersistentStoreCoordinator = [self buildPersistentStoreCoordinator];
}
return _mainQueuePersistentStoreCoordinator;
}
- (NSPersistentStoreCoordinator *)privateQueuePersistentStoreCoordinator
{
if(_privateQueuePersistentStoreCoordinator == nil)
{
_privateQueuePersistentStoreCoordinator = [self buildPersistentStoreCoordinator];
}
return _privateQueuePersistentStoreCoordinator;
}
- (NSManagedObjectContext *)mainQueueManagedObjectContext
{
if(_mainQueueManagedObjectContext == nil)
{
_mainQueueManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
_mainQueueManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
_mainQueueManagedObjectContext.persistentStoreCoordinator = self.mainQueuePersistentStoreCoordinator;
[[_mainQueueManagedObjectContext userInfo] setObject:@"Main Thread" forKey:@"mocIdentifier"];
}
return _mainQueueManagedObjectContext;
}
- (NSManagedObjectContext *)privateQueueManagedObjectContext
{
if(_privateQueueManagedObjectContext == nil)
{
_privateQueueManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
_privateQueueManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
_privateQueueManagedObjectContext.persistentStoreCoordinator = self.privateQueuePersistentStoreCoordinator;
[[_privateQueueManagedObjectContext userInfo] setObject:@"Private Queue" forKey:@"mocIdentifier"];
}
return _privateQueueManagedObjectContext;
}
- (NSManagedObjectContext *)childContextWithConcurrencyType:(NSManagedObjectContextConcurrencyType)type
{
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:type];
context.parentContext = self.privateQueueManagedObjectContext;
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
[context userInfo][@"mocIdentifier"] = @"Child Context";
return context;
}
- (void)handleManagedObjectContextDidSaveNotification:(NSNotification *)notification
{
NSManagedObjectContext *saveContext = [notification object];
NSString *contextIdentifier = [saveContext userInfo][@"mocIdentifier"];
NSLog(@"did save notification from %@", contextIdentifier);
if(saveContext == self.mainQueueManagedObjectContext)
{
[self.privateQueueManagedObjectContext mergeChangesFromContextDidSaveNotification:notification];
[self saveMasterContext];
}
else if(saveContext == self.privateQueueManagedObjectContext)
{
[self.mainQueueManagedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}
else if([saveContext parentContext] == self.privateQueueManagedObjectContext)
{
[self saveMasterContext];
[self.mainQueueManagedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}
}
- (void)handleDidEnterBackgroundNotification:(NSNotification *)notification
{
[self saveMasterContext];
}
- (void)saveMasterContext
{
if([self.privateQueueManagedObjectContext hasChanges])
{
UIBackgroundTaskIdentifier identifier = [[UIApplication sharedApplication] beginBackgroundTaskWithName:@"background-moc-save" expirationHandler:^{
NSLog(@"we expired");
}];
if(identifier != UIBackgroundTaskInvalid)
{
[self.privateQueueManagedObjectContext performBlock:^{
[self.privateQueueManagedObjectContext save:nil];
NSLog(@"saved background context");
[[UIApplication sharedApplication] endBackgroundTask:identifier];
}];
}
}
}
- (NSPersistentStoreCoordinator *)buildPersistentStoreCoordinator
{
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];
NSError *error = nil;
NSPersistentStore *store = [psc addPersistentStoreWithType:self.storeType configuration:self.modelConfiguration URL:self.storeURL options:self.storeOptions error:&error];
if(!store && [self.delegate respondsToSelector:@selector(retryAfterPersistentStoreCreationFailedWithError:coreDataController:)])
{
BOOL retry = [self.delegate retryAfterPersistentStoreCreationFailedWithError:error coreDataController:self];
if(retry)
{
store = [psc addPersistentStoreWithType:self.storeType configuration:self.modelConfiguration URL:self.storeURL options:self.storeOptions error:NULL];
}
}
return psc;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment