2 // CoreDataManager.swift
5 // Created by Hori,Masaki on 2017/02/05.
6 // Copyright © 2017年 Hori,Masaki. All rights reserved.
11 enum CoreDataManagerType {
16 enum CoreDataError: Error {
17 case applicationDirectoryIsFile
18 case couldNotCreateModel
19 case couldNotCreateCoordinator(String)
22 struct CoreDataIntormation {
24 let storeFileName: String
25 let storeOptions: Dictionary<AnyHashable, Any>
27 let deleteAndRetry: Bool
31 let info: CoreDataIntormation
32 let managedObjectModel: NSManagedObjectModel
33 let persistentStoreCoordinator: NSPersistentStoreCoordinator
34 let parentManagedObjectContext: NSManagedObjectContext
36 init(_ info: CoreDataIntormation) {
38 let genaratee = try! MocGenerater.genarate(info)
39 self.managedObjectModel = genaratee.model
40 self.persistentStoreCoordinator = genaratee.coordinator
41 self.parentManagedObjectContext = genaratee.moc
44 func editorManagedObjectContext() -> NSManagedObjectContext {
45 let moc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
46 moc.parent = parentManagedObjectContext
56 protocol CoreDataProvider {
57 init(type: CoreDataManagerType)
58 var core: CoreDataCore { get }
59 var managedObjectContext: NSManagedObjectContext { get }
63 protocol CoreDataManager {
64 associatedtype InstanceType = Self
66 static var `default`: InstanceType { get }
67 static func oneTimeEditor() -> InstanceType
69 func removeDatabaseFile()
72 protocol CoreDataAccessor: CoreDataProvider {
73 func insertNewObject(for entity: Entity) -> NSManagedObject
74 func delete(_ object: NSManagedObject)
75 func object(with objectId: NSManagedObjectID) -> NSManagedObject
76 func objects(with entity: Entity, sortDescriptors: [NSSortDescriptor]?, predicate: NSPredicate?) throws -> [NSManagedObject]
79 private class CoreDataRemover {
80 class func remove(name: String) {
83 .map { ApplicationDirecrories.support.appendingPathComponent($0) }
84 .forEach { removeDatabaseFileAtURL(url: $0) }
86 private class func removeDatabaseFileAtURL(url: URL) {
88 try FileManager.default.removeItem(at: url)
91 print("Could not remove file for URL (\(url))");
96 private class MocGenerater {
97 class func genarate(_ info: CoreDataIntormation) throws -> (model: NSManagedObjectModel, coordinator: NSPersistentStoreCoordinator, moc: NSManagedObjectContext) {
99 let model = try createManagedObjectModel(info)
100 let coordinator = try createPersistentStoreCoordinator(info, model)
101 let moc = createManagedObjectContext(coordinator)
102 return (model: model, coordinator: coordinator, moc: moc)
109 private class func createManagedObjectModel(_ info: CoreDataIntormation) throws -> NSManagedObjectModel {
110 let modelURL = Bundle.main.url(forResource: info.modelName, withExtension: "momd")!
111 guard let model = NSManagedObjectModel(contentsOf: modelURL) else {
112 throw CoreDataError.couldNotCreateModel
117 private class func createPersistentStoreCoordinator(_ info: CoreDataIntormation, _ model: NSManagedObjectModel) throws -> NSPersistentStoreCoordinator {
118 var failError: NSError? = nil
119 var shouldFail = false
120 var failureReason = "There was an error creating or loading the application's saved data."
123 let p = try ApplicationDirecrories.support.resourceValues(forKeys: [.isDirectoryKey])
125 failureReason = "Expected a folder to store application data, found a file \(ApplicationDirecrories.support.path)."
130 let nserror = error as NSError
131 if nserror.code == NSFileReadNoSuchFileError {
133 try FileManager.default.createDirectory(at: ApplicationDirecrories.support, withIntermediateDirectories: false, attributes: nil)
142 var coordinator: NSPersistentStoreCoordinator? = nil
143 if failError == nil {
144 coordinator = NSPersistentStoreCoordinator(managedObjectModel: model)
145 let url = ApplicationDirecrories.support.appendingPathComponent(info.storeFileName)
147 try coordinator!.addPersistentStore(ofType: info.storeType, configurationName: nil, at: url, options: info.storeOptions)
149 failError = error as NSError
151 // Data Modelが更新されていたらストアファイルを削除してもう一度
152 if failError?.domain == NSCocoaErrorDomain && (failError?.code == 134130 || failError?.code == 134110) && info.deleteAndRetry {
153 self.removeDatabaseFile(info)
155 try coordinator!.addPersistentStore(ofType: info.storeType, configurationName: nil, at: url, options: info.storeOptions)
159 failError = error as NSError
165 if shouldFail || (failError != nil) {
166 if let error = failError {
167 NSApplication.shared().presentError(error)
169 throw CoreDataError.couldNotCreateCoordinator(failureReason)
173 private class func createManagedObjectContext(_ coordinator: NSPersistentStoreCoordinator) -> NSManagedObjectContext {
174 let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
175 moc.persistentStoreCoordinator = coordinator
176 moc.undoManager = nil
179 private class func removeDatabaseFile(_ info: CoreDataIntormation) {
180 CoreDataRemover.remove(name: info.storeFileName)
184 extension CoreDataManager where Self: CoreDataProvider {
185 func removeDatabaseFile() {
186 CoreDataRemover.remove(name: self.core.info.storeFileName)
190 extension CoreDataProvider {
191 func saveActionCore() {
192 if !managedObjectContext.commitEditing() {
193 NSLog("\(String(describing: type(of: self))) unable to commit editing before saveing")
196 do { try managedObjectContext.save() }
197 catch { presentOnMainThread(error) }
198 if let p = managedObjectContext.parent {
201 catch { self.presentOnMainThread(error) }
205 private func presentOnMainThread(_ error: Error) {
206 if Thread.isMainThread {
207 NSApp.presentError(error)
209 DispatchQueue.main.sync {
210 let _ = NSApp.presentError(error)
216 extension CoreDataAccessor {
217 func insertNewObject(for entity: Entity) -> NSManagedObject {
218 return NSEntityDescription.insertNewObject(forEntityName: entity.name, into: managedObjectContext)
220 func delete(_ object: NSManagedObject) {
221 managedObjectContext.delete(object)
223 func object(with objectId: NSManagedObjectID) -> NSManagedObject {
224 return managedObjectContext.object(with: objectId)
226 func objects(with entity: Entity, sortDescriptors: [NSSortDescriptor]? = nil, predicate: NSPredicate? = nil) throws -> [NSManagedObject] {
227 let req = NSFetchRequest<NSManagedObject>(entityName: entity.name)
228 req.sortDescriptors = sortDescriptors
229 req.predicate = predicate
230 return try managedObjectContext.fetch(req)