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
53 protocol CoreDataProvider {
54 init(type: CoreDataManagerType)
55 var core: CoreDataCore { get }
56 var managedObjectContext: NSManagedObjectContext { get }
60 protocol CoreDataManager {
61 associatedtype InstanceType = Self
63 static var `default`: InstanceType { get }
64 static func oneTimeEditor() -> InstanceType
66 func removeDatabaseFile()
69 protocol CoreDataAccessor: CoreDataProvider {
70 func insertNewObject<T>(for entity: Entity<T>) -> T?
71 func delete(_ object: NSManagedObject)
72 func object(with objectId: NSManagedObjectID) -> NSManagedObject
73 func objects<T>(with entity: Entity<T>, sortDescriptors: [NSSortDescriptor]?, predicate: NSPredicate?) throws -> [T]
76 private class CoreDataRemover {
77 class func remove(name: String) {
80 .map { ApplicationDirecrories.support.appendingPathComponent($0) }
81 .forEach { removeDatabaseFileAtURL(url: $0) }
83 private class func removeDatabaseFileAtURL(url: URL) {
85 try FileManager.default.removeItem(at: url)
88 print("Could not remove file for URL (\(url))");
93 private class MocGenerater {
94 class func genarate(_ info: CoreDataIntormation) throws -> (model: NSManagedObjectModel, coordinator: NSPersistentStoreCoordinator, moc: NSManagedObjectContext) {
96 let model = try createManagedObjectModel(info)
97 let coordinator = try createPersistentStoreCoordinator(info, model)
98 let moc = createManagedObjectContext(coordinator)
99 return (model: model, coordinator: coordinator, moc: moc)
106 private class func createManagedObjectModel(_ info: CoreDataIntormation) throws -> NSManagedObjectModel {
107 let modelURL = Bundle.main.url(forResource: info.modelName, withExtension: "momd")!
108 guard let model = NSManagedObjectModel(contentsOf: modelURL) else {
109 throw CoreDataError.couldNotCreateModel
114 private class func createPersistentStoreCoordinator(_ info: CoreDataIntormation, _ model: NSManagedObjectModel) throws -> NSPersistentStoreCoordinator {
115 var failError: NSError? = nil
116 var shouldFail = false
117 var failureReason = "There was an error creating or loading the application's saved data."
120 let p = try ApplicationDirecrories.support.resourceValues(forKeys: [.isDirectoryKey])
122 failureReason = "Expected a folder to store application data, found a file \(ApplicationDirecrories.support.path)."
127 let nserror = error as NSError
128 if nserror.code == NSFileReadNoSuchFileError {
130 try FileManager.default.createDirectory(at: ApplicationDirecrories.support, withIntermediateDirectories: false, attributes: nil)
139 var coordinator: NSPersistentStoreCoordinator? = nil
140 if failError == nil {
141 coordinator = NSPersistentStoreCoordinator(managedObjectModel: model)
142 let url = ApplicationDirecrories.support.appendingPathComponent(info.storeFileName)
144 try coordinator!.addPersistentStore(ofType: info.storeType, configurationName: nil, at: url, options: info.storeOptions)
146 failError = error as NSError
148 // Data Modelが更新されていたらストアファイルを削除してもう一度
149 if failError?.domain == NSCocoaErrorDomain && (failError?.code == 134130 || failError?.code == 134110) && info.deleteAndRetry {
150 self.removeDatabaseFile(info)
152 try coordinator!.addPersistentStore(ofType: info.storeType, configurationName: nil, at: url, options: info.storeOptions)
156 failError = error as NSError
162 if shouldFail || (failError != nil) {
163 if let error = failError {
164 NSApplication.shared().presentError(error)
166 throw CoreDataError.couldNotCreateCoordinator(failureReason)
170 private class func createManagedObjectContext(_ coordinator: NSPersistentStoreCoordinator) -> NSManagedObjectContext {
171 let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
172 moc.persistentStoreCoordinator = coordinator
173 moc.undoManager = nil
176 private class func removeDatabaseFile(_ info: CoreDataIntormation) {
177 CoreDataRemover.remove(name: info.storeFileName)
181 extension CoreDataManager where Self: CoreDataProvider {
182 func removeDatabaseFile() {
183 CoreDataRemover.remove(name: self.core.info.storeFileName)
187 extension CoreDataProvider {
188 func saveActionCore() {
189 if !managedObjectContext.commitEditing() {
190 NSLog("\(String(describing: type(of: self))) unable to commit editing before saveing")
193 do { try managedObjectContext.save() }
194 catch { presentOnMainThread(error) }
195 if let p = managedObjectContext.parent {
198 catch { self.presentOnMainThread(error) }
202 private func presentOnMainThread(_ error: Error) {
203 if Thread.isMainThread {
204 NSApp.presentError(error)
206 DispatchQueue.main.sync {
207 let _ = NSApp.presentError(error)
213 extension CoreDataAccessor {
214 func insertNewObject<T>(for entity: Entity<T>) -> T? {
215 return NSEntityDescription
216 .insertNewObject(forEntityName: entity.name
217 , into: managedObjectContext) as? T
219 func delete(_ object: NSManagedObject) {
220 managedObjectContext.delete(object)
222 func object(with objectId: NSManagedObjectID) -> NSManagedObject {
223 return managedObjectContext.object(with: objectId)
225 func objects<T>(with entity: Entity<T>, sortDescriptors: [NSSortDescriptor]? = nil, predicate: NSPredicate? = nil) throws -> [T] {
226 let req = NSFetchRequest<T>(entityName: entity.name)
227 req.sortDescriptors = sortDescriptors
228 req.predicate = predicate
229 return try managedObjectContext.fetch(req)