OSDN Git Service

guard の書き方を統一した
[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     
14     case reader
15     case editor
16 }
17
18 // MARK: - struct
19 struct CoreDataConfiguration {
20     
21     let modelName: String
22     let fileName: String
23     let options: [AnyHashable: Any]
24     let type: String
25     let tryRemake: Bool
26     
27     private static let defaultOptions: [AnyHashable: Any] = [
28         NSMigratePersistentStoresAutomaticallyOption: true,
29         NSInferMappingModelAutomaticallyOption: true
30     ]
31     
32     init(_ modelName: String,
33          fileName: String? = nil,
34          options: [AnyHashable: Any] = defaultOptions,
35          type: String = NSSQLiteStoreType,
36          tryRemake: Bool = false) {
37         
38         self.modelName = modelName
39         self.fileName = fileName ?? "\(modelName).storedata"
40         self.options = options
41         self.type = type
42         self.tryRemake = tryRemake
43     }
44 }
45
46 struct CoreDataCore {
47     
48     let config: CoreDataConfiguration
49     let parentContext: NSManagedObjectContext
50     private let model: NSManagedObjectModel
51     private let coordinator: NSPersistentStoreCoordinator
52     
53     init(_ config: CoreDataConfiguration) {
54         
55         self.config = config
56         
57         do {
58             
59             (model, coordinator, parentContext) = try genarate(config)
60             
61         } catch {
62             
63             NSApplication.shared.presentError(error)
64             fatalError("CoreDataCore: can not initialize. \(error)")
65         }
66     }
67     
68     func editorContext() -> NSManagedObjectContext {
69         
70         let moc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
71         moc.parent = parentContext
72         moc.undoManager = nil
73         
74         return moc
75     }
76 }
77
78 // MARK: - protocol
79 protocol CoreDataProvider {
80     
81     init(type: CoreDataManagerType)
82     
83     static var core: CoreDataCore { get }
84     
85     var context: NSManagedObjectContext { get }
86     
87     func save()
88     func removeDataFile()
89 }
90
91 protocol CoreDataAccessor: CoreDataProvider {
92     
93     func insertNewObject<T>(for entity: Entity<T>) -> T?
94     func delete(_ object: NSManagedObject)
95     func object<T>(of entity: Entity<T>, with objectId: NSManagedObjectID) -> T?
96     func objects<T>(of entity: Entity<T>, sortDescriptors: [NSSortDescriptor]?, predicate: NSPredicate?) throws -> [T]
97     
98     func object(with objectId: NSManagedObjectID) -> NSManagedObject
99 }
100
101 protocol CoreDataManager {
102     
103     associatedtype InstanceType = Self
104     
105     static var `default`: InstanceType { get }
106     
107     static func oneTimeEditor() -> InstanceType
108 }
109
110 // MARK: - Extension
111 extension CoreDataProvider {
112     
113     func save() {
114         
115         if !context.commitEditing() {
116             
117             print("\(String(describing: type(of: self))) unable to commit editing before saveing")
118             
119             return
120         }
121         
122         do {
123             
124             try context.save()
125             
126         } catch {
127             
128             presentOnMainThread(error)
129         }
130         
131         if let p = context.parent {
132             
133             p.performAndWait {
134                 do {
135                     
136                     try p.save()
137                     
138                 } catch {
139                     
140                     self.presentOnMainThread(error)
141                 }
142             }
143         }
144     }
145     
146     func removeDataFile() {
147         
148         remove(name: type(of: self).core.config.fileName)
149     }
150     
151     private func presentOnMainThread(_ error: Error) {
152         
153         if Thread.isMainThread {
154             
155             NSApp.presentError(error)
156             
157         } else {
158             
159             DispatchQueue.main.sync {
160                 
161                 _ = NSApp.presentError(error)
162             }
163         }
164     }
165     
166     static func context(for type: CoreDataManagerType) -> NSManagedObjectContext {
167         
168         switch type {
169         case .reader:
170             return core.parentContext
171             
172         case .editor:
173             return core.editorContext()
174         }
175     }
176 }
177
178 extension CoreDataAccessor {
179     
180     func insertNewObject<T>(for entity: Entity<T>) -> T? {
181         
182         return NSEntityDescription.insertNewObject(forEntityName: entity.name, into: context) as? T
183     }
184     
185     func delete(_ object: NSManagedObject) {
186         
187         context.delete(object)
188     }
189     
190     func object<T>(of entity: Entity<T>, with objectId: NSManagedObjectID) -> T? {
191         
192         return context.object(with: objectId) as? T
193     }
194     
195     func objects<T>(of entity: Entity<T>, sortDescriptors: [NSSortDescriptor]? = nil, predicate: NSPredicate? = nil) throws -> [T] {
196         
197         let req = NSFetchRequest<T>(entityName: entity.name)
198         req.sortDescriptors = sortDescriptors
199         req.predicate = predicate
200         
201         return try context.fetch(req)
202     }
203     
204     func object(with objectId: NSManagedObjectID) -> NSManagedObject {
205         
206         return context.object(with: objectId)
207     }
208 }