OSDN Git Service

関数を分割して簡略化
[kcd/KCD.git] / KCD / CoreDataCore.swift
index 304e9b8..71215bc 100644 (file)
@@ -21,10 +21,10 @@ enum CoreDataError: Error {
 
 struct CoreDataIntormation {
     let modelName: String
-    let storeFileName: String
-    let storeOptions: Dictionary<AnyHashable, Any>
-    let storeType: String
-    let deleteAndRetry: Bool
+    let fileName: String
+    let options: [AnyHashable: Any]
+    let type: String
+    let tryRemake: Bool
     
     private static let defaultOptions: [AnyHashable: Any] = [
         NSMigratePersistentStoresAutomaticallyOption: true,
@@ -32,35 +32,38 @@ struct CoreDataIntormation {
     ]
     
     init(_ modelName: String,
-         storeFileName: String? = nil,
-         storeOptions: [AnyHashable: Any] = defaultOptions,
-         storeType: String = NSSQLiteStoreType,
-         deleteAndRetry: Bool = false) {
+         fileName: String? = nil,
+         options: [AnyHashable: Any] = defaultOptions,
+         type: String = NSSQLiteStoreType,
+         tryRemake: Bool = false) {
         self.modelName = modelName
-        self.storeFileName = storeFileName ?? "\(modelName).storedata"
-        self.storeOptions = storeOptions
-        self.storeType = storeType
-        self.deleteAndRetry = deleteAndRetry
+        self.fileName = fileName ?? "\(modelName).storedata"
+        self.options = options
+        self.type = type
+        self.tryRemake = tryRemake
     }
 }
 
 struct CoreDataCore {
     let info: CoreDataIntormation
-    let managedObjectModel: NSManagedObjectModel
-    let persistentStoreCoordinator: NSPersistentStoreCoordinator
-    let parentManagedObjectContext: NSManagedObjectContext
+    let model: NSManagedObjectModel
+    let coordinator: NSPersistentStoreCoordinator
+    let parentContext: NSManagedObjectContext
     
     init(_ info: CoreDataIntormation) {
         self.info = info
-        let genaratee = try! MocGenerater.genarate(info)
-        self.managedObjectModel = genaratee.model
-        self.persistentStoreCoordinator = genaratee.coordinator
-        self.parentManagedObjectContext = genaratee.moc
+        do {
+            let genaratee = try MocGenerater.genarate(info)
+            (self.model, self.coordinator, self.parentContext) = genaratee
+        } catch {
+            NSApplication.shared().presentError(error)
+            fatalError("CoreDataCore: can not initialize. \(error)")
+        }
     }
     
-    func editorManagedObjectContext() -> NSManagedObjectContext {
+    func editorContext() -> NSManagedObjectContext {
         let moc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
-        moc.parent = parentManagedObjectContext
+        moc.parent = parentContext
         moc.undoManager = nil
         return moc
     }
@@ -70,8 +73,8 @@ struct CoreDataCore {
 protocol CoreDataProvider {
     init(type: CoreDataManagerType)
     var core: CoreDataCore { get }
-    var managedObjectContext: NSManagedObjectContext { get }
-    func saveActionCore()
+    var context: NSManagedObjectContext { get }
+    func save()
 }
 
 protocol CoreDataManager {
@@ -80,7 +83,7 @@ protocol CoreDataManager {
     static var `default`: InstanceType { get }
     static func oneTimeEditor() -> InstanceType
     
-    func removeDatabaseFile()
+    func removeDataFile()
 }
 
 protocol CoreDataAccessor: CoreDataProvider {
@@ -95,124 +98,107 @@ private class CoreDataRemover {
         ["", "-wal", "-shm"]
             .map { name + $0 }
             .map { ApplicationDirecrories.support.appendingPathComponent($0) }
-            .forEach { removeDatabaseFileAtURL(url: $0) }
+            .forEach { removeDataFile(at: $0) }
     }
-    private class func removeDatabaseFileAtURL(url: URL) {
+    private class func removeDataFile(at url: URL) {
         do {
             try FileManager.default.removeItem(at: url)
-        }
-        catch {
-            print("Could not remove file for URL (\(url))");
+        } catch {
+            print("Could not remove file for URL (\(url))")
         }
     }
 }
 
 private class MocGenerater {
-    class func genarate(_ info: CoreDataIntormation) throws -> (model: NSManagedObjectModel, coordinator: NSPersistentStoreCoordinator, moc: NSManagedObjectContext) {
-        do {
-            let model = try createManagedObjectModel(info)
-            let coordinator = try createPersistentStoreCoordinator(info, model)
-            let moc = createManagedObjectContext(coordinator)
-            return (model: model, coordinator: coordinator, moc: moc)
-        }
-        catch {
-            throw error
-        }
+    class func genarate(_ info: CoreDataIntormation) throws ->
+        (model: NSManagedObjectModel, coordinator: NSPersistentStoreCoordinator, moc: NSManagedObjectContext) {
+            do {
+                let model = try createModel(info)
+                let coordinator = try getCoordinator(info, model)
+                let moc = createContext(coordinator)
+                return (model: model, coordinator: coordinator, moc: moc)
+            } catch {
+                throw error
+            }
     }
     
-    private class func createManagedObjectModel(_ info: CoreDataIntormation) throws -> NSManagedObjectModel {
+    private class func createModel(_ info: CoreDataIntormation) throws -> NSManagedObjectModel {
         let modelURL = Bundle.main.url(forResource: info.modelName, withExtension: "momd")!
         guard let model = NSManagedObjectModel(contentsOf: modelURL) else {
             throw CoreDataError.couldNotCreateModel
         }
         return model
     }
-    
-    private class func createPersistentStoreCoordinator(_ info: CoreDataIntormation, _ model: NSManagedObjectModel) throws -> NSPersistentStoreCoordinator {
-        var failError: NSError? = nil
-        var shouldFail = false
-        var failureReason = "There was an error creating or loading the application's saved data."
-        
+    private class func getCoordinator(_ info: CoreDataIntormation,
+                                      _ model: NSManagedObjectModel) throws -> NSPersistentStoreCoordinator {
         do {
-            let p = try ApplicationDirecrories.support.resourceValues(forKeys: [.isDirectoryKey])
-            if !p.isDirectory! {
-                failureReason = "Expected a folder to store application data, found a file \(ApplicationDirecrories.support.path)."
-                shouldFail = true
-            }
-        }
-        catch {
+            return try createCoordinator(info, model)
+        } catch {
             let nserror = error as NSError
-            if nserror.code == NSFileReadNoSuchFileError {
+            // Data Modelが更新されていたらストアファイルを削除してもう一度
+            if nserror.domain == NSCocoaErrorDomain,
+                (nserror.code == 134130 || nserror.code == 134110),
+                info.tryRemake {
+                self.removeDataFile(info)
                 do {
-                    try FileManager.default.createDirectory(at: ApplicationDirecrories.support, withIntermediateDirectories: false, attributes: nil)
+                    return try createCoordinator(info, model)
                 } catch {
-                    failError = nserror
+                    print("Fail crrate NSPersistentStoreCoordinator twice.")
                 }
-            } else {
-                failError = nserror
             }
+            throw error
         }
-        
-        var coordinator: NSPersistentStoreCoordinator? = nil
-        if failError == nil {
-            coordinator = NSPersistentStoreCoordinator(managedObjectModel: model)
-            let url = ApplicationDirecrories.support.appendingPathComponent(info.storeFileName)
-            do {
-                try coordinator!.addPersistentStore(ofType: info.storeType, configurationName: nil, at: url, options: info.storeOptions)
-            } catch {
-                failError = error as NSError
-                
-                // Data Modelが更新されていたらストアファイルを削除してもう一度
-                if failError?.domain == NSCocoaErrorDomain && (failError?.code == 134130 || failError?.code == 134110) && info.deleteAndRetry {
-                    self.removeDatabaseFile(info)
-                    do {
-                        try coordinator!.addPersistentStore(ofType: info.storeType, configurationName: nil, at: url, options: info.storeOptions)
-                        failError = nil
-                    }
-                    catch {
-                        failError = error as NSError
-                    }
-                }
-            }
+    }
+    private class func createCoordinator(_ info: CoreDataIntormation,
+                                         _ model: NSManagedObjectModel) throws -> NSPersistentStoreCoordinator {
+        if !checkDirectory(ApplicationDirecrories.support) {
+            let failureReason = "Can not use directory \(ApplicationDirecrories.support.path)"
+            throw CoreDataError.couldNotCreateCoordinator(failureReason)
         }
         
-        if shouldFail || (failError != nil) {
-            if let error = failError {
-                NSApplication.shared().presentError(error)
-            }
-            throw CoreDataError.couldNotCreateCoordinator(failureReason)
+        let coordinator: NSPersistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: model)
+        let url = ApplicationDirecrories.support.appendingPathComponent(info.fileName)
+        do {
+            try coordinator.addPersistentStore(ofType: info.type,
+                                               configurationName: nil,
+                                               at: url,
+                                               options: info.options)
+        } catch {
+            throw error
         }
-        return coordinator!
+        return coordinator
     }
-    private class func createManagedObjectContext(_ coordinator: NSPersistentStoreCoordinator) -> NSManagedObjectContext {
+    private class func createContext(_ coordinator: NSPersistentStoreCoordinator) -> NSManagedObjectContext {
         let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
         moc.persistentStoreCoordinator = coordinator
         moc.undoManager = nil
         return moc
     }
-    private class func removeDatabaseFile(_ info: CoreDataIntormation) {
-        CoreDataRemover.remove(name: info.storeFileName)
+    private class func removeDataFile(_ info: CoreDataIntormation) {
+        CoreDataRemover.remove(name: info.fileName)
     }
 }
 
 extension CoreDataManager where Self: CoreDataProvider {
-    func removeDatabaseFile() {
-        CoreDataRemover.remove(name: self.core.info.storeFileName)
+    func removeDataFile() {
+        CoreDataRemover.remove(name: self.core.info.fileName)
     }
 }
 
 extension CoreDataProvider {
-    func saveActionCore() {
-        if !managedObjectContext.commitEditing() {
-            NSLog("\(String(describing: type(of: self))) unable to commit editing before saveing")
+    func save() {
+        if !context.commitEditing() {
+            print("\(String(describing: type(of: self))) unable to commit editing before saveing")
             return
         }
-        do { try managedObjectContext.save() }
-        catch { presentOnMainThread(error) }
-        if let p = managedObjectContext.parent {
+        do {
+            try context.save()
+        } catch { presentOnMainThread(error) }
+        if let p = context.parent {
             p.performAndWait {
-                do { try p.save() }
-                catch { self.presentOnMainThread(error) }
+                do {
+                    try p.save()
+                } catch { self.presentOnMainThread(error) }
             }
         }
     }
@@ -230,19 +216,21 @@ extension CoreDataProvider {
 extension CoreDataAccessor {
     func insertNewObject<T>(for entity: Entity<T>) -> T? {
         return NSEntityDescription
-            .insertNewObject(forEntityName: entity.name
-                , into: managedObjectContext) as? T
+            .insertNewObject(forEntityName: entity.name,
+                             into: context) as? T
     }
     func delete(_ object: NSManagedObject) {
-        managedObjectContext.delete(object)
+        context.delete(object)
     }
     func object(with objectId: NSManagedObjectID) -> NSManagedObject {
-        return managedObjectContext.object(with: objectId)
+        return context.object(with: objectId)
     }
-    func objects<T>(with entity: Entity<T>, sortDescriptors: [NSSortDescriptor]? = nil, predicate: NSPredicate? = nil) throws -> [T] {
+    func objects<T>(with entity: Entity<T>,
+                    sortDescriptors: [NSSortDescriptor]? = nil,
+                    predicate: NSPredicate? = nil) throws -> [T] {
         let req = NSFetchRequest<T>(entityName: entity.name)
         req.sortDescriptors = sortDescriptors
         req.predicate = predicate
-        return try managedObjectContext.fetch(req)
+        return try context.fetch(req)
     }
 }