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 {
25 let options: [AnyHashable: Any]
29 private static let defaultOptions: [AnyHashable: Any] = [
30 NSMigratePersistentStoresAutomaticallyOption: true,
31 NSInferMappingModelAutomaticallyOption: true
34 init(_ modelName: String,
35 fileName: String? = nil,
36 options: [AnyHashable: Any] = defaultOptions,
37 type: String = NSSQLiteStoreType,
38 tryRemake: Bool = false) {
39 self.modelName = modelName
40 self.fileName = fileName ?? "\(modelName).storedata"
41 self.options = options
43 self.tryRemake = tryRemake
48 let info: CoreDataIntormation
49 let model: NSManagedObjectModel
50 let coordinator: NSPersistentStoreCoordinator
51 let parentContext: NSManagedObjectContext
53 init(_ info: CoreDataIntormation) {
56 let genaratee = try MocGenerater.genarate(info)
57 (self.model, self.coordinator, self.parentContext) = genaratee
59 NSApplication.shared().presentError(error)
60 fatalError("CoreDataCore: can not initialize. \(error)")
64 func editorContext() -> NSManagedObjectContext {
65 let moc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
66 moc.parent = parentContext
73 protocol CoreDataProvider {
74 init(type: CoreDataManagerType)
75 var core: CoreDataCore { get }
76 var context: NSManagedObjectContext { get }
80 protocol CoreDataManager {
81 associatedtype InstanceType = Self
83 static var `default`: InstanceType { get }
84 static func oneTimeEditor() -> InstanceType
89 protocol CoreDataAccessor: CoreDataProvider {
90 func insertNewObject<T>(for entity: Entity<T>) -> T?
91 func delete(_ object: NSManagedObject)
92 func object(with objectId: NSManagedObjectID) -> NSManagedObject
93 func objects<T>(with entity: Entity<T>, sortDescriptors: [NSSortDescriptor]?, predicate: NSPredicate?) throws -> [T]
96 private class CoreDataRemover {
97 class func remove(name: String) {
100 .map { ApplicationDirecrories.support.appendingPathComponent($0) }
101 .forEach { removeDataFile(at: $0) }
103 private class func removeDataFile(at url: URL) {
105 try FileManager.default.removeItem(at: url)
107 print("Could not remove file for URL (\(url))")
112 private class MocGenerater {
113 class func genarate(_ info: CoreDataIntormation) throws ->
114 (model: NSManagedObjectModel, coordinator: NSPersistentStoreCoordinator, moc: NSManagedObjectContext) {
116 let model = try createModel(info)
117 let coordinator = try getCoordinator(info, model)
118 let moc = createContext(coordinator)
119 return (model: model, coordinator: coordinator, moc: moc)
125 private class func createModel(_ info: CoreDataIntormation) throws -> NSManagedObjectModel {
126 let modelURL = Bundle.main.url(forResource: info.modelName, withExtension: "momd")!
127 guard let model = NSManagedObjectModel(contentsOf: modelURL) else {
128 throw CoreDataError.couldNotCreateModel
132 private class func getCoordinator(_ info: CoreDataIntormation,
133 _ model: NSManagedObjectModel) throws -> NSPersistentStoreCoordinator {
135 return try createCoordinator(info, model)
137 let nserror = error as NSError
138 // Data Modelが更新されていたらストアファイルを削除してもう一度
139 if nserror.domain == NSCocoaErrorDomain,
140 (nserror.code == 134130 || nserror.code == 134110),
142 self.removeDataFile(info)
144 return try createCoordinator(info, model)
146 print("Fail crrate NSPersistentStoreCoordinator twice.")
152 private class func createCoordinator(_ info: CoreDataIntormation,
153 _ model: NSManagedObjectModel) throws -> NSPersistentStoreCoordinator {
154 if !checkDirectory(ApplicationDirecrories.support) {
155 let failureReason = "Can not use directory \(ApplicationDirecrories.support.path)"
156 throw CoreDataError.couldNotCreateCoordinator(failureReason)
159 let coordinator: NSPersistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: model)
160 let url = ApplicationDirecrories.support.appendingPathComponent(info.fileName)
162 try coordinator.addPersistentStore(ofType: info.type,
163 configurationName: nil,
165 options: info.options)
171 private class func createContext(_ coordinator: NSPersistentStoreCoordinator) -> NSManagedObjectContext {
172 let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
173 moc.persistentStoreCoordinator = coordinator
174 moc.undoManager = nil
177 private class func removeDataFile(_ info: CoreDataIntormation) {
178 CoreDataRemover.remove(name: info.fileName)
182 extension CoreDataManager where Self: CoreDataProvider {
183 func removeDataFile() {
184 CoreDataRemover.remove(name: self.core.info.fileName)
188 extension CoreDataProvider {
190 if !context.commitEditing() {
191 print("\(String(describing: type(of: self))) unable to commit editing before saveing")
196 } catch { presentOnMainThread(error) }
197 if let p = context.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<T>(for entity: Entity<T>) -> T? {
218 return NSEntityDescription
219 .insertNewObject(forEntityName: entity.name,
222 func delete(_ object: NSManagedObject) {
223 context.delete(object)
225 func object(with objectId: NSManagedObjectID) -> NSManagedObject {
226 return context.object(with: objectId)
228 func objects<T>(with entity: Entity<T>,
229 sortDescriptors: [NSSortDescriptor]? = nil,
230 predicate: NSPredicate? = nil) throws -> [T] {
231 let req = NSFetchRequest<T>(entityName: entity.name)
232 req.sortDescriptors = sortDescriptors
233 req.predicate = predicate
234 return try context.fetch(req)