OSDN Git Service

HMMasterMissionCommandクラスをSwiftで書き換え
[kcd/KCD.git] / KCD / HMCoreDataManager.swift
1 //
2 //  HMCoreDataManager.swift
3 //  KCD
4 //
5 //  Created by Hori,Masaki on 2015/01/03.
6 //  Copyright (c) 2015年 Hori,Masaki. All rights reserved.
7 //
8
9 import Cocoa
10
11 private var defaultManagers: [NSObject : NSObject]! = nil
12 private var _managedObjectModelStore: [NSObject : NSManagedObjectModel]! = nil
13 private var _persistentStoreCoordinatorStore: [NSObject : NSPersistentStoreCoordinator]! = nil
14
15 private func managedObjectModelStore() -> [NSObject : NSManagedObjectModel] {
16         if _managedObjectModelStore == nil {
17                 _managedObjectModelStore = [:]
18         }
19         return _managedObjectModelStore
20 }
21 private func persistentStoreCoordinatorStore() -> [NSObject : NSPersistentStoreCoordinator] {
22         if _persistentStoreCoordinatorStore == nil {
23                 _persistentStoreCoordinatorStore = [:]
24         }
25         return _persistentStoreCoordinatorStore
26 }
27
28 class HMCoreDataManager: NSObject {
29         enum HMCoreDataManagerType {
30                 case reader
31                 case editor
32         }
33         
34         required init(type: HMCoreDataManagerType) {
35                 self.type = type
36                 super.init()
37         }
38         deinit {
39                 saveAction(nil)
40         }
41         
42         let type: HMCoreDataManagerType
43         
44         private class func defaultManagerStore() -> [NSObject : NSObject] {
45                 if defaultManagers == nil {
46                         defaultManagers = [:]
47                 }
48                 return defaultManagers
49         }
50         private class func storedDefaultManager<T>(type : T.Type) -> T? {
51                 let classname = NSStringFromClass(self)
52                 if let stored = defaultManagerStore()[classname] as? T {
53                         return stored
54                 }
55                 
56                 return nil
57         }
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
62                 }
63         }
64         class func defaultManager() -> Self {
65                 if let stored = storedDefaultManager(self) {
66                         return stored
67                 }
68                 
69                 let defaultManager = self(type: .reader)
70                 defaultManager.managedObjectContext!.stalenessInterval = 0.0
71                 
72                 let nc = NSNotificationCenter.defaultCenter()
73                 nc.addObserverForName(
74                         NSApplicationWillTerminateNotification,
75                         object: NSApp,
76                         queue: NSOperationQueue.mainQueue()) {
77                                 (notification: NSNotification!) in
78                                 defaultManager.saveAction(nil)
79                 }
80                 
81                 storeDefaultManager(self, defaultManager: defaultManager)
82                 return defaultManager
83         }
84         
85         class func oneTimeEditor() -> Self {
86                 return self(type: .editor)
87         }
88         
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")
93                 }()
94         
95         lazy var managedObjectModel: NSManagedObjectModel = {
96                 var stored = managedObjectModelStore()[NSStringFromClass(self.dynamicType)]
97                 if stored != nil { return stored! as NSManagedObjectModel }
98                 
99                 let modelURL = NSBundle.mainBundle().URLForResource(self.modelName(), withExtension: "momd")!
100                 let managedObjectModel = NSManagedObjectModel(contentsOfURL: modelURL)!
101                 
102                 _managedObjectModelStore[NSStringFromClass(self.dynamicType)] = managedObjectModel
103                 
104                 return managedObjectModel
105                 }()
106         
107         lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = {
108                 var stored = persistentStoreCoordinatorStore()[NSStringFromClass(self.dynamicType)]
109                 if stored != nil { return stored }
110                 
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."
116                 
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)."
122                                 shouldFail = true
123                         }
124                 } else if error!.code == NSFileReadNoSuchFileError {
125                         error = nil
126                         fileManager.createDirectoryAtPath(self.applicationDocumentsDirectory.path!, withIntermediateDirectories: true, attributes: nil, error: &error)
127                 }
128                 
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)
134                 if store == nil {
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)
138                                 if store == nil {
139                                         NSApplication.sharedApplication().presentError(error!)
140                                         return nil
141                                 }
142                         } else {
143                                 NSApplication.sharedApplication().presentError(error!)
144                         }
145                 }
146                 if coordinator != nil {
147                         _persistentStoreCoordinatorStore[NSStringFromClass(self.dynamicType)] = coordinator
148                 }
149                 
150                 return coordinator
151                 }()
152         
153         lazy var managedObjectContext: NSManagedObjectContext? = {
154                 var managedObjectContext: NSManagedObjectContext? = nil
155                 switch self.type {
156                 case .reader:
157                         let coordinator = self.persistentStoreCoordinator
158                         if coordinator == nil {
159                                 return nil
160                         }
161                         managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
162                         managedObjectContext?.persistentStoreCoordinator = coordinator
163                 case .editor:
164                         managedObjectContext = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
165                         managedObjectContext?.parentContext = self.dynamicType.defaultManager().managedObjectContext
166                 }
167                 managedObjectContext?.undoManager = nil
168                 
169                 return managedObjectContext
170                 }()
171         
172         
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")
177                         }
178                         var error: NSError? = nil
179                         if moc.hasChanges && !moc.save(&error) {
180                                 if NSThread.isMainThread() {
181                                         NSApplication.sharedApplication().presentError(error!)
182                                 } else {
183                                         dispatch_sync(dispatch_get_main_queue()) {
184                                                 let e: NSError = error!
185                                                 NSApplication.sharedApplication().presentError(e)
186                                         }
187                                 }
188                         }
189                 }
190         }
191 }
192
193 // MARK: - Abstruct
194 extension HMCoreDataManager {
195         func modelName() -> String {
196                 assertionFailure("MUST OVERRIDE THIS")
197         }
198         func storeFileName() -> String {
199                 assertionFailure("MUST OVERRIDE THIS")
200         }
201         func storeType() -> String {
202                 assertionFailure("MUST OVERRIDE THIS")
203         }
204         func storeOptions() -> [NSObject : AnyObject]? {
205                 assertionFailure("MUST OVERRIDE THIS")
206         }
207         func deleteAndRetry() -> Bool {
208                 assertionFailure("MUST OVERRIDE THIS")
209         }
210 }
211
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) {
219                         return array
220                 }
221                 return []
222         }
223 }