2 // HMCoreDataManager.swift
5 // Created by Hori,Masaki on 2015/01/03.
6 // Copyright (c) 2015年 Hori,Masaki. All rights reserved.
11 private var defaultManagers: [NSObject : NSObject]! = nil
12 private var _managedObjectModelStore: [NSObject : NSManagedObjectModel]! = nil
13 private var _persistentStoreCoordinatorStore: [NSObject : NSPersistentStoreCoordinator]! = nil
15 private func managedObjectModelStore() -> [NSObject : NSManagedObjectModel] {
16 if _managedObjectModelStore == nil {
17 _managedObjectModelStore = [:]
19 return _managedObjectModelStore
21 private func persistentStoreCoordinatorStore() -> [NSObject : NSPersistentStoreCoordinator] {
22 if _persistentStoreCoordinatorStore == nil {
23 _persistentStoreCoordinatorStore = [:]
25 return _persistentStoreCoordinatorStore
28 class HMCoreDataManager: NSObject {
29 enum HMCoreDataManagerType {
34 required init(type: HMCoreDataManagerType) {
42 let type: HMCoreDataManagerType
44 private class func defaultManagerStore() -> [NSObject : NSObject] {
45 if defaultManagers == nil {
48 return defaultManagers
50 private class func storedDefaultManager<T>(type : T.Type) -> T? {
51 let classname = NSStringFromClass(self)
52 if let stored = defaultManagerStore()[classname] as? T {
58 private class func storeDefaultManager<T>(type: T.Type, defaultManager: T) {
59 let classname = NSStringFromClass(self)
60 if let obj = defaultManager as? NSObject {
61 defaultManagers[classname] = obj
64 class func defaultManager() -> Self {
65 if let stored = storedDefaultManager(self) {
69 let defaultManager = self(type: .reader)
70 defaultManager.managedObjectContext!.stalenessInterval = 0.0
72 let nc = NSNotificationCenter.defaultCenter()
73 nc.addObserverForName(
74 NSApplicationWillTerminateNotification,
76 queue: NSOperationQueue.mainQueue()) {
77 (notification: NSNotification!) in
78 defaultManager.saveAction(nil)
81 storeDefaultManager(self, defaultManager: defaultManager)
85 class func oneTimeEditor() -> Self {
86 return self(type: .editor)
89 lazy var applicationDocumentsDirectory: NSURL = {
90 let urls = NSFileManager.defaultManager().URLsForDirectory(.ApplicationSupportDirectory, inDomains: .UserDomainMask)
91 let appSupportURL = urls[urls.count - 1] as NSURL
92 return appSupportURL.URLByAppendingPathComponent("com.masakih.KCD")
95 lazy var managedObjectModel: NSManagedObjectModel = {
96 var stored = managedObjectModelStore()[NSStringFromClass(self.dynamicType)]
97 if stored != nil { return stored! as NSManagedObjectModel }
99 let modelURL = NSBundle.mainBundle().URLForResource(self.modelName(), withExtension: "momd")!
100 let managedObjectModel = NSManagedObjectModel(contentsOfURL: modelURL)!
102 _managedObjectModelStore[NSStringFromClass(self.dynamicType)] = managedObjectModel
104 return managedObjectModel
107 lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = {
108 var stored = persistentStoreCoordinatorStore()[NSStringFromClass(self.dynamicType)]
109 if stored != nil { return stored }
111 // 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.) This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
112 let fileManager = NSFileManager.defaultManager()
113 var shouldFail = false
114 var error: NSError? = nil
115 var failureReason = "There was an error creating or loading the application's saved data."
117 // Make sure the application files directory is there
118 let propertiesOpt = self.applicationDocumentsDirectory.resourceValuesForKeys([NSURLIsDirectoryKey], error: &error)
119 if let properties = propertiesOpt {
120 if !properties[NSURLIsDirectoryKey]!.boolValue {
121 failureReason = "Expected a folder to store application data, found a file \(self.applicationDocumentsDirectory.path)."
124 } else if error!.code == NSFileReadNoSuchFileError {
126 fileManager.createDirectoryAtPath(self.applicationDocumentsDirectory.path!, withIntermediateDirectories: true, attributes: nil, error: &error)
129 // Create the coordinator and store
130 var coordinator: NSPersistentStoreCoordinator? = nil
131 coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
132 let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent(self.storeFileName())
133 var store = coordinator!.addPersistentStoreWithType(self.storeType(), configuration: nil, URL: url, options: self.storeOptions(), error: &error)
135 if error != nil && error!.domain == NSCocoaErrorDomain && error?.code == 134130 && self.deleteAndRetry() {
136 fileManager.removeItemAtURL(url, error: &error)
137 store = coordinator!.addPersistentStoreWithType(self.storeType(), configuration: nil, URL: url, options: self.storeOptions(), error: &error)
139 NSApplication.sharedApplication().presentError(error!)
143 NSApplication.sharedApplication().presentError(error!)
146 if coordinator != nil {
147 _persistentStoreCoordinatorStore[NSStringFromClass(self.dynamicType)] = coordinator
153 lazy var managedObjectContext: NSManagedObjectContext? = {
154 var managedObjectContext: NSManagedObjectContext? = nil
157 let coordinator = self.persistentStoreCoordinator
158 if coordinator == nil {
161 managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
162 managedObjectContext?.persistentStoreCoordinator = coordinator
164 managedObjectContext = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
165 managedObjectContext?.parentContext = self.dynamicType.defaultManager().managedObjectContext
167 managedObjectContext?.undoManager = nil
169 return managedObjectContext
173 @IBAction func saveAction(sender: AnyObject?) {
174 if let moc = self.managedObjectContext {
175 if !moc.commitEditing() {
176 NSLog("\(NSStringFromClass(self.dynamicType)) unable to commit editing before saving")
178 var error: NSError? = nil
179 if moc.hasChanges && !moc.save(&error) {
180 if NSThread.isMainThread() {
181 NSApplication.sharedApplication().presentError(error!)
183 dispatch_sync(dispatch_get_main_queue()) {
184 let e: NSError = error!
185 NSApplication.sharedApplication().presentError(e)
194 extension HMCoreDataManager {
195 func modelName() -> String {
196 assertionFailure("MUST OVERRIDE THIS")
198 func storeFileName() -> String {
199 assertionFailure("MUST OVERRIDE THIS")
201 func storeType() -> String {
202 assertionFailure("MUST OVERRIDE THIS")
204 func storeOptions() -> [NSObject : AnyObject]? {
205 assertionFailure("MUST OVERRIDE THIS")
207 func deleteAndRetry() -> Bool {
208 assertionFailure("MUST OVERRIDE THIS")
212 // MARK: - CoreData Accessor
213 extension HMCoreDataManager {
214 func objectsWithEntityName(entityName: String, sortDescriptors: [NSSortDescriptor]? = nil, predicate: NSPredicate? = nil, error: NSErrorPointer = nil) -> [AnyObject] {
215 let request = NSFetchRequest(entityName: entityName)
216 request.predicate = predicate
217 request.sortDescriptors = sortDescriptors
218 if let array = managedObjectContext?.executeFetchRequest(request, error: error) {