OSDN Git Service

データファイルを作成し直すマイグレーションエラーの範囲を広げた
[kcd/KCD.git] / KCD / MOCGenerator.swift
1 //
2 //  ManagedObjectContextGenerator.swift
3 //  KCD
4 //
5 //  Created by Hori,Masaki on 2017/03/21.
6 //  Copyright © 2017年 Hori,Masaki. All rights reserved.
7 //
8
9 import Cocoa
10
11 final class MOCGenerator {
12     
13     let config: CoreDataConfiguration
14     
15     init(_ config: CoreDataConfiguration) {
16         
17         self.config = config
18     }
19     
20     func genarate() throws -> (model: NSManagedObjectModel, coordinator: NSPersistentStoreCoordinator, moc: NSManagedObjectContext) {
21         
22         let model = try createModel()
23         let coordinator = try createCoordinator(model)
24         let moc = createContext(coordinator)
25         
26         return (model: model, coordinator: coordinator, moc: moc)
27     }
28     
29     func removeDataFile() {
30         
31         ["", "-wal", "-shm"]
32             .map { config.fileName + $0 }
33             .map(ApplicationDirecrories.support.appendingPathComponent)
34             .forEach(removeFile)
35     }
36     
37     private func removeFile(at url: URL) {
38         
39         do {
40             
41             try FileManager.default.removeItem(at: url)
42             
43         } catch {
44             
45             print("Could not remove file for URL (\(url))")
46         }
47     }
48     
49     // MARK: - NSManagedObjectContext and ...
50     private func createModel() throws -> NSManagedObjectModel {
51         
52         guard let modelURL = Bundle.main.url(forResource: config.modelName, withExtension: "momd"),
53             let model = NSManagedObjectModel(contentsOf: modelURL) else {
54                 
55                 throw CoreDataError.couldNotCreateModel
56         }
57         
58         return model
59     }
60     
61     private func isMigrationError(_ code: Int) -> Bool {
62         return [
63             NSPersistentStoreIncompatibleVersionHashError,
64             NSMigrationError,
65             NSMigrationConstraintViolationError,
66             NSMigrationCancelledError,
67             NSMigrationMissingSourceModelError,
68             NSMigrationMissingMappingModelError,
69             NSMigrationManagerSourceStoreError,
70             NSMigrationManagerDestinationStoreError,
71             NSEntityMigrationPolicyError
72         ].contains(code)
73     }
74     
75     private func createCoordinator(_ model: NSManagedObjectModel) throws -> NSPersistentStoreCoordinator {
76         
77         if !checkDirectory(ApplicationDirecrories.support) {
78             
79             throw CoreDataError.saveLocationIsUnuseable
80         }
81         
82         do {
83             
84             return try makeCoordinator(model)
85             
86         } catch (let error as NSError) {
87             
88             // Data Modelが更新されていたらストアファイルを削除してもう一度
89             if error.domain == NSCocoaErrorDomain,
90                 isMigrationError(error.code),
91                 config.tryRemake {
92                 
93                 removeDataFile()
94                 
95                 do {
96                     
97                     return try makeCoordinator(model)
98                     
99                 } catch {
100                     
101                     print("Fail to create NSPersistentStoreCoordinator twice.")
102                 }
103             }
104             
105             throw CoreDataError.couldNotCreateCoordinator(error.localizedDescription)
106         }
107     }
108     
109     private func makeCoordinator(_ model: NSManagedObjectModel) throws -> NSPersistentStoreCoordinator {
110         
111         let coordinator: NSPersistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: model)
112         let url = ApplicationDirecrories.support.appendingPathComponent(config.fileName)
113         try coordinator.addPersistentStore(ofType: config.type,
114                                            configurationName: nil,
115                                            at: url,
116                                            options: config.options)
117         
118         return coordinator
119     }
120     
121     private func createContext(_ coordinator: NSPersistentStoreCoordinator) -> NSManagedObjectContext {
122         
123         let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
124         moc.persistentStoreCoordinator = coordinator
125         moc.undoManager = nil
126         
127         return moc
128     }
129 }