OSDN Git Service

バージョンを1.9b10に更新
[kcd/KCD.git] / KCD / CoreDataCore.swift
1 //
2 //  CoreDataManager.swift
3 //  KCD
4 //
5 //  Created by Hori,Masaki on 2017/02/05.
6 //  Copyright © 2017年 Hori,Masaki. All rights reserved.
7 //
8
9 import Cocoa
10
11 // MARK: enum
12 enum CoreDataManagerType {
13     case reader
14     case editor
15 }
16
17 // MARK: - struct
18 struct CoreDataConfiguration {
19     let modelName: String
20     let fileName: String
21     let options: [AnyHashable: Any]
22     let type: String
23     let tryRemake: Bool
24     
25     private static let defaultOptions: [AnyHashable: Any] = [
26         NSMigratePersistentStoresAutomaticallyOption: true,
27         NSInferMappingModelAutomaticallyOption: true
28     ]
29     
30     init(_ modelName: String,
31          fileName: String? = nil,
32          options: [AnyHashable: Any] = defaultOptions,
33          type: String = NSSQLiteStoreType,
34          tryRemake: Bool = false) {
35         self.modelName = modelName
36         self.fileName = fileName ?? "\(modelName).storedata"
37         self.options = options
38         self.type = type
39         self.tryRemake = tryRemake
40     }
41 }
42
43 struct CoreDataCore {
44     let config: CoreDataConfiguration
45     let parentContext: NSManagedObjectContext
46     private let model: NSManagedObjectModel
47     private let coordinator: NSPersistentStoreCoordinator
48     
49     init(_ config: CoreDataConfiguration) {
50         self.config = config
51         do {
52             (model, coordinator, parentContext) = try genarate(config)
53         } catch {
54             NSApplication.shared().presentError(error)
55             fatalError("CoreDataCore: can not initialize. \(error)")
56         }
57     }
58     
59     func editorContext() -> NSManagedObjectContext {
60         let moc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
61         moc.parent = parentContext
62         moc.undoManager = nil
63         return moc
64     }
65 }
66
67 // MARK: - protocol
68 protocol CoreDataProvider {
69     init(type: CoreDataManagerType)
70     static var core: CoreDataCore { get }
71     var context: NSManagedObjectContext { get }
72     func save()
73     func removeDataFile()
74 }
75
76 protocol CoreDataAccessor: CoreDataProvider {
77     func insertNewObject<T>(for entity: Entity<T>) -> T?
78     func delete(_ object: NSManagedObject)
79     func object(with objectId: NSManagedObjectID) -> NSManagedObject
80     func objects<T>(with entity: Entity<T>, sortDescriptors: [NSSortDescriptor]?, predicate: NSPredicate?) throws -> [T]
81 }
82
83 protocol CoreDataManager {
84     associatedtype InstanceType = Self
85     
86     static var `default`: InstanceType { get }
87     static func oneTimeEditor() -> InstanceType
88 }
89
90 // MARK: - Extension
91 extension CoreDataProvider {
92     func save() {
93         if !context.commitEditing() {
94             print("\(String(describing: type(of: self))) unable to commit editing before saveing")
95             return
96         }
97         do {
98             try context.save()
99         } catch { presentOnMainThread(error) }
100         if let p = context.parent {
101             p.performAndWait {
102                 do {
103                     try p.save()
104                 } catch {
105                     self.presentOnMainThread(error)
106                 }
107             }
108         }
109     }
110     func removeDataFile() {
111         remove(name: type(of: self).core.config.fileName)
112     }
113     private func presentOnMainThread(_ error: Error) {
114         if Thread.isMainThread {
115             NSApp.presentError(error)
116         } else {
117             DispatchQueue.main.sync {
118                 let _ = NSApp.presentError(error)
119             }
120         }
121     }
122     
123     static func context(for type: CoreDataManagerType) -> NSManagedObjectContext {
124         switch type {
125         case .reader:
126             return core.parentContext
127         case .editor:
128             return core.editorContext()
129         }
130     }
131 }
132
133 extension CoreDataAccessor {
134     func insertNewObject<T>(for entity: Entity<T>) -> T? {
135         return NSEntityDescription.insertNewObject(forEntityName: entity.name, into: context) as? T
136     }
137     func delete(_ object: NSManagedObject) {
138         context.delete(object)
139     }
140     func object(with objectId: NSManagedObjectID) -> NSManagedObject {
141         return context.object(with: objectId)
142     }
143     func objects<T>(with entity: Entity<T>,
144                     sortDescriptors: [NSSortDescriptor]? = nil,
145                     predicate: NSPredicate? = nil) throws -> [T] {
146         let req = NSFetchRequest<T>(entityName: entity.name)
147         req.sortDescriptors = sortDescriptors
148         req.predicate = predicate
149         return try context.fetch(req)
150     }
151 }