5 // Created by Hori,Masaki on 2014/02/20.
6 // Copyright (c) 2014年 Hori,Masaki. All rights reserved.
9 #import "HMCoreDataManager.h"
11 #import <objc/runtime.h>
13 typedef NS_ENUM(NSUInteger, HMCoreDataManagerType) {
18 @interface HMCoreDataManager ()
19 @property HMCoreDataManagerType type;
22 @implementation HMCoreDataManager
24 @synthesize managedObjectContext = _managedObjectContext;
26 + (instancetype)defaultManager
28 HMCoreDataManager *defaultManager = objc_getAssociatedObject(self, "defaultManager");
30 if(defaultManager) return defaultManager;
32 defaultManager = [self new];
33 defaultManager.type = readerType;
35 [[defaultManager managedObjectContext] setStalenessInterval:0.0];
37 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
38 [nc addObserver:defaultManager
39 selector:@selector(applicationWillTerminate:)
40 name:NSApplicationWillTerminateNotification
43 objc_setAssociatedObject(self, "defaultManager", defaultManager, OBJC_ASSOCIATION_RETAIN);
44 return defaultManager;
47 + (instancetype)oneTimeEditor
49 HMCoreDataManager *result = [self new];
50 result.type = editorType;
57 [self saveAction:nil];
60 - (NSArray *)objectsWithEntityName:(NSString *)entityName sortDescriptors:(NSArray *)sortDescriptors predicate:(NSPredicate *)predicate error:(NSError **)error
62 NSFetchRequest *req = [NSFetchRequest fetchRequestWithEntityName:entityName];
63 [req setPredicate:predicate];
64 [req setSortDescriptors:sortDescriptors];
66 NSArray *array = [self.managedObjectContext executeFetchRequest:req error:error];
69 - (NSArray *)objectsWithEntityName:(NSString *)entityName sortDescriptors:(NSArray *)sortDescriptors error:(NSError **)error predicateFormat:(NSString *)format, ...
73 NSPredicate *predicate = [NSPredicate predicateWithFormat:format arguments:ap];
75 return [self objectsWithEntityName:entityName sortDescriptors:sortDescriptors predicate:predicate error:error];
78 - (NSArray *)objectsWithEntityName:(NSString *)entityName predicate:(NSPredicate *)predicate error:(NSError **)error
80 return [self objectsWithEntityName:entityName sortDescriptors:nil predicate:predicate error:error];
82 - (NSArray *)objectsWithEntityName:(NSString *)entityName error:(NSError **)error predicateFormat:(NSString *)format, ...
86 NSPredicate *predicate = [NSPredicate predicateWithFormat:format arguments:ap];
88 return [self objectsWithEntityName:entityName sortDescriptors:nil predicate:predicate error:error];
91 // Returns the directory the application uses to store the Core Data store file. This code uses a directory named "com.masakih.KanColleLevelManager" in the user's Application Support directory.
92 - (NSURL *)applicationFilesDirectory
94 NSFileManager *fileManager = [NSFileManager defaultManager];
95 NSURL *appSupportURL = [[fileManager URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask] lastObject];
96 return [appSupportURL URLByAppendingPathComponent:@"com.masakih.KCD"];
99 // Creates if necessary and returns the managed object model for the application.
100 - (NSManagedObjectModel *)managedObjectModel
102 id managedObjectModel = objc_getAssociatedObject([self class], "managedObjectModel");
103 if (managedObjectModel) {
104 return managedObjectModel;
107 NSURL *modelURL = [[NSBundle mainBundle] URLForResource:self.modelName withExtension:@"momd"];
108 managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
109 objc_setAssociatedObject([self class], "managedObjectModel", managedObjectModel, OBJC_ASSOCIATION_RETAIN);
110 return managedObjectModel;
113 // Returns the persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. (The directory for the store is created, if necessary.)
114 - (NSPersistentStoreCoordinator *)persistentStoreCoordinator
116 id persistentStoreCoordinator = objc_getAssociatedObject([self class], "persistentStoreCoordinator");
117 if (persistentStoreCoordinator) {
118 return persistentStoreCoordinator;
121 NSManagedObjectModel *mom = [self managedObjectModel];
123 NSLog(@"%@:%@ No model to generate a store from", [self class], NSStringFromSelector(_cmd));
127 NSFileManager *fileManager = [NSFileManager defaultManager];
128 NSURL *applicationFilesDirectory = [self applicationFilesDirectory];
129 NSError *error = nil;
131 NSDictionary *properties = [applicationFilesDirectory resourceValuesForKeys:@[NSURLIsDirectoryKey] error:&error];
135 if ([error code] == NSFileReadNoSuchFileError) {
136 ok = [fileManager createDirectoryAtPath:[applicationFilesDirectory path] withIntermediateDirectories:YES attributes:nil error:&error];
139 [[NSApplication sharedApplication] presentError:error];
143 if (![properties[NSURLIsDirectoryKey] boolValue]) {
144 // Customize and localize this error.
145 NSString *failureDescription = [NSString stringWithFormat:@"Expected a folder to store application data, found a file (%@).", [applicationFilesDirectory path]];
147 NSMutableDictionary *dict = [NSMutableDictionary dictionary];
148 [dict setValue:failureDescription forKey:NSLocalizedDescriptionKey];
149 error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:101 userInfo:dict];
151 [[NSApplication sharedApplication] presentError:error];
155 NSURL *url = [applicationFilesDirectory URLByAppendingPathComponent:self.storeFileName];
156 NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
157 NSPersistentStore *store = [coordinator addPersistentStoreWithType:self.storeType
160 options:self.storeOptions
163 // Data Modelが更新されていたらストアファイルを削除してもう一度
164 if([[error domain] isEqualToString:NSCocoaErrorDomain] && [error code] == 134130 && self.deleteAndRetry) {
165 [self removeDatabaseFile];
166 store = [coordinator addPersistentStoreWithType:self.storeType
169 options:self.storeOptions
172 [[NSApplication sharedApplication] presentError:error];
176 [[NSApplication sharedApplication] presentError:error];
181 objc_setAssociatedObject([self class], "persistentStoreCoordinator", coordinator, OBJC_ASSOCIATION_RETAIN);
186 // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
187 - (NSManagedObjectContext *)managedObjectContext
189 if (_managedObjectContext) {
190 return _managedObjectContext;
193 if(self.type == readerType) {
194 NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
196 NSMutableDictionary *dict = [NSMutableDictionary dictionary];
197 [dict setValue:@"Failed to initialize the store" forKey:NSLocalizedDescriptionKey];
198 [dict setValue:@"There was an error building up the data file." forKey:NSLocalizedFailureReasonErrorKey];
199 NSError *error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
200 [[NSApplication sharedApplication] presentError:error];
203 _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
204 [_managedObjectContext setPersistentStoreCoordinator:coordinator];
206 _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
207 _managedObjectContext.parentContext = [[[self class] defaultManager] managedObjectContext];
210 _managedObjectContext.undoManager = nil;
212 return _managedObjectContext;
215 // Performs the save action for the application, which is to send the save: message to the application's managed object context. Any encountered errors are presented to the user.
216 - (IBAction)saveAction:(id)sender
218 NSError *error = nil;
220 if (![[self managedObjectContext] commitEditing]) {
221 NSLog(@"%@:%@ unable to commit editing before saving", [self class], NSStringFromSelector(_cmd));
224 if (![[self managedObjectContext] save:&error]) {
225 if([NSThread isMainThread]) {
226 [[NSApplication sharedApplication] presentError:error];
228 dispatch_sync(dispatch_get_main_queue(), ^{
229 [[NSApplication sharedApplication] presentError:error];
234 NSManagedObjectContext *mainContext = self.managedObjectContext.parentContext;
235 [mainContext performBlock:^{ // do nothing if mainContext is nil.
236 NSError *error = nil;
237 if(![mainContext save:&error]) {
238 if([NSThread isMainThread]) {
239 [[NSApplication sharedApplication] presentError:error];
241 dispatch_sync(dispatch_get_main_queue(), ^{
242 [[NSApplication sharedApplication] presentError:error];
249 - (void)applicationWillTerminate:(NSNotification *)notification
251 [self saveAction:nil];
255 - (void)removeDatabaseFileAtURL:(NSURL *)url
257 NSFileManager *fileManager = [NSFileManager defaultManager];
259 const char *filesystemRep = url.fileSystemRepresentation;
260 NSString *path = [NSString stringWithUTF8String:filesystemRep];
261 if(![fileManager fileExistsAtPath:path]) {
262 NSLog(@"Could not find file for url (%@)", url);
265 NSError *error = nil;
266 [fileManager removeItemAtURL:url error:&error];
268 NSLog(@"Could not remove file for URL (%@)", url);
271 - (void)removeDatabaseFile
273 NSURL *applicationFilesDirectory = [self applicationFilesDirectory];
274 NSString *baseName = self.storeFileName;
276 for(NSString *suffix in @[@"", @"-wal", @"-shm"]) {
277 NSString *fileName = [baseName stringByAppendingString:suffix];
278 NSURL *url = [applicationFilesDirectory URLByAppendingPathComponent:fileName];
279 [self removeDatabaseFileAtURL:url];
283 #pragma mark - abstruct
284 - (NSString *)modelName
286 [NSException raise:@"Abstract method" format:@"%s is abstract.", __PRETTY_FUNCTION__];
289 - (NSString *)storeFileName
291 [NSException raise:@"Abstract method" format:@"%s is abstract.", __PRETTY_FUNCTION__];
294 - (NSString *)storeType
296 [NSException raise:@"Abstract method" format:@"%s is abstract.", __PRETTY_FUNCTION__];
299 - (NSDictionary *)storeOptions
301 [NSException raise:@"Abstract method" format:@"%s is abstract.", __PRETTY_FUNCTION__];
304 - (BOOL)deleteAndRetry
306 [NSException raise:@"Abstract method" format:@"%s is abstract.", __PRETTY_FUNCTION__];