IOS-1633 cleanup IOS-1633
authorbuff <andreas@pep-project.org>
Fri, 04 Oct 2019 12:34:24 +0200
branchIOS-1633
changeset 4503afd651e0ec26
parent 4502 5f5efd3a8bff
child 4504 0f7f6d3d480d
IOS-1633 cleanup
MessageModel/MessageModel/Submodule/CoreData/Stack/Stack.swift
     1.1 --- a/MessageModel/MessageModel/Submodule/CoreData/Stack/Stack.swift	Fri Oct 04 12:34:05 2019 +0200
     1.2 +++ b/MessageModel/MessageModel/Submodule/CoreData/Stack/Stack.swift	Fri Oct 04 12:34:24 2019 +0200
     1.3 @@ -1,307 +1,3 @@
     1.4 -/* 1633 stack
     1.5 - //
     1.6 -// Stack.swift
     1.7 -//
     1.8 -//  Created by Andreas Buff on 03.07.19.
     1.9 -//  Copyright © 2019 p≡p Security S.A. All rights reserved.
    1.10 -//
    1.11 -
    1.12 -import CoreData
    1.13 -
    1.14 -import pEpIOSToolbox
    1.15 -
    1.16 -/// Our Core Data Stack
    1.17 -class Stack {
    1.18 -
    1.19 -    // MARK: - Singleton
    1.20 -
    1.21 -    static private(set) var shared = Stack() {
    1.22 -        didSet {
    1.23 -            guard MiscUtil.isUnitTest() else {
    1.24 -                fatalError("This dirty trick is allowed in unit tests only")
    1.25 -            }
    1.26 -        }
    1.27 -    }
    1.28 -
    1.29 -    private init() {
    1.30 -        do {
    1.31 -            if MiscUtil.isUnitTest() {
    1.32 -                // Uses in memory store for tests. No file -> no need to bother with file.
    1.33 -                try loadCoreDataStack(storeType: NSInMemoryStoreType)
    1.34 -            } else {
    1.35 -                try loadCoreDataStack()
    1.36 -            }
    1.37 -        } catch {
    1.38 -            fatalError("No Stack, no running app, sorry.")
    1.39 -        }
    1.40 -    }
    1.41 -
    1.42 -    // MARK: - Properties
    1.43 -
    1.44 -    private var model: NSManagedObjectModel?
    1.45 -    private var coordinator: NSPersistentStoreCoordinator?
    1.46 -
    1.47 -    var mainContext: NSManagedObjectContext!
    1.48 -    fileprivate var changePropagatorContext: NSManagedObjectContext! //make fileprivate //BUFF: rename, docs
    1.49 -
    1.50 -    var newPrivateConcurrentContext: NSManagedObjectContext {
    1.51 -        objc_sync_enter(self)
    1.52 -
    1.53 -        let createe = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
    1.54 -        createe.parent = changePropagatorContext
    1.55 -        createe.name = "privateQueueConcurrent context created on \(Date())"
    1.56 -        createe.automaticallyMergesChangesFromParent = true
    1.57 -        createe.undoManager = nil
    1.58 -        createe.mergePolicy = Stack.objectWinsMergePolicy
    1.59 -
    1.60 -        objc_sync_exit(self)
    1.61 -
    1.62 -        return createe
    1.63 -
    1.64 -    }
    1.65 -}
    1.66 -
    1.67 -// MARK: - Setup
    1.68 -
    1.69 -extension Stack {
    1.70 -
    1.71 -    /// Returns the final URL for the store with given name.
    1.72 -    ///
    1.73 -    /// - parameter name: Filename for the store.
    1.74 -    /// - returns: File URL for the store with given name.
    1.75 -    //!!!: make private after Record is gone
    1.76 -    static func storeURL(for name: String) -> URL {
    1.77 -        let fileManager = FileManager.default
    1.78 -        let directoryURL = fileManager.urls(for: defaultDirectory, in: .userDomainMask).last!
    1.79 -        let storeName = "\(name).sqlite"
    1.80 -        return directoryURL.appendingPathComponent(storeName)
    1.81 -    }
    1.82 -
    1.83 -    /// Loads Core Data Stack (creates new if it doesn't already exist) with given options (all
    1.84 -    /// options are optional).
    1.85 -    ///
    1.86 -    /// - NOTE:
    1.87 -    /// Default option for `managedObjectModel` is `NSManagedObjectModel.mergedModelFromBundles(nil)!`,
    1.88 -    /// custom may be provided by using `modelFromBundle:` method.
    1.89 -    ///
    1.90 -    /// Default option for `storeType` is `NSSQLiteStoreType`
    1.91 -    ///
    1.92 -    /// Default option for `storeURL` is `bundleIdentifier + ".sqlite"` inside
    1.93 -    /// `applicationDocumentsDirectory`, custom may be provided by using `storeURLForName:` method.
    1.94 -    ///
    1.95 -    /// - parameter managedObjectModel: Managed object model for Core Data Stack.
    1.96 -    /// - parameter storeType: Store type for Persistent Store creation.
    1.97 -    /// - parameter configuration: Configuration for Persistent Store creation.
    1.98 -    /// - parameter storeURL: File URL for Persistent Store creation.
    1.99 -    /// - parameter options: Options for Persistent Store creation.
   1.100 -    ///
   1.101 -    /// - returns: Throws error if something went wrong.
   1.102 -    func loadCoreDataStack(managedObjectModel: NSManagedObjectModel = defaultManagedObjectModel,
   1.103 -                           storeType: String = NSSQLiteStoreType,
   1.104 -                           configuration: String? = nil,
   1.105 -                           storeURL: URL = defaultURL,
   1.106 -                           options: [AnyHashable : Any]? = defaultOptions) throws {
   1.107 -        model = managedObjectModel
   1.108 -        try configureStoreCoordinator(model: managedObjectModel,
   1.109 -                                      type: storeType,
   1.110 -                                      configuration: configuration,
   1.111 -                                      url: storeURL,
   1.112 -                                      options: options)
   1.113 -        configureManagedObjectContexts()
   1.114 -    }
   1.115 -
   1.116 -    private func configureManagedObjectContexts() {
   1.117 -        guard coordinator != nil else {
   1.118 -            Log.shared.errorAndCrash("We MUST have a cooridinator @ this point")
   1.119 -            return
   1.120 -        }
   1.121 -        mainContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
   1.122 -        mainContext.persistentStoreCoordinator = coordinator
   1.123 -        mainContext.name = "mainContext"
   1.124 -        mainContext.undoManager = nil
   1.125 -        mainContext.mergePolicy = Stack.objectWinsMergePolicy
   1.126 -
   1.127 -        changePropagatorContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
   1.128 -        changePropagatorContext.parent = mainContext
   1.129 -        changePropagatorContext.name = "changePropagatorContext"
   1.130 -        changePropagatorContext.automaticallyMergesChangesFromParent = true
   1.131 -        changePropagatorContext.undoManager = nil
   1.132 -        changePropagatorContext.mergePolicy = Stack.objectWinsMergePolicy
   1.133 -    }
   1.134 -
   1.135 -    private func configureStoreCoordinator(model: NSManagedObjectModel,
   1.136 -                                           type: String,
   1.137 -                                           configuration: String?,
   1.138 -                                           url: URL,
   1.139 -                                           options: [AnyHashable : Any]?) throws {
   1.140 -        coordinator = NSPersistentStoreCoordinator(managedObjectModel: model)
   1.141 -        try coordinator?.addPersistentStore(ofType: type,
   1.142 -                                            configurationName: configuration,
   1.143 -                                            at: url,
   1.144 -                                            options: options)
   1.145 -    }
   1.146 -}
   1.147 -
   1.148 -// MARK: - Tear Down
   1.149 -//!!!: move to test MM target after pEp4IOS stopped using CD
   1.150 -extension Stack {
   1.151 -
   1.152 -    /// Resets the Stack.
   1.153 -    /// MUST be used in tests only.
   1.154 -    /// - note: After calling this method, you MUST NOT use any privateConcurrentContext that has been created before calling this method.
   1.155 -    func reset() {
   1.156 -        objc_sync_enter(Stack.unitTestLock)
   1.157 -        defer { objc_sync_exit(Stack.unitTestLock) }
   1.158 -        guard MiscUtil.isUnitTest() else { fatalError("Not permitted to use in production code.") }
   1.159 -
   1.160 -        mainContext.persistentStoreCoordinator?.persistentStores.forEach {
   1.161 -            do {
   1.162 -                try $0.persistentStoreCoordinator?.destroyPersistentStore(at: Stack.defaultURL,
   1.163 -                                                                          ofType: NSInMemoryStoreType,
   1.164 -                                                                          options: nil)
   1.165 -            } catch {
   1.166 -                Log.shared.errorAndCrash(error: error)
   1.167 -            }
   1.168 -        }
   1.169 -        mainContext.persistentStoreCoordinator = nil
   1.170 -        mainContext = nil
   1.171 -        changePropagatorContext = nil
   1.172 -
   1.173 -        Stack.shared = Stack()
   1.174 -
   1.175 -    }
   1.176 -}
   1.177 -
   1.178 -//!!!!: move! Or better: RE-move
   1.179 -
   1.180 -extension Stack {
   1.181 -
   1.182 -    func execute<T: NSManagedObject>(fetchRequest request: NSFetchRequest<T>,
   1.183 -                                     in context: NSManagedObjectContext) -> [T] {
   1.184 -        do {
   1.185 -            return try context.fetch(request)
   1.186 -        } catch {
   1.187 -            Log.shared.errorAndCrash(error: error)
   1.188 -        }
   1.189 -        return []
   1.190 -    }
   1.191 -
   1.192 -    //MOVE:
   1.193 -    //!!!: must go away with mergeChanges
   1.194 -    /// Locks to assure nothing is called concurrently ito reset() (in unit tests)
   1.195 -    static let unitTestLock = NSObject()
   1.196 -}
   1.197 -
   1.198 -// MARK: - Defaults
   1.199 -
   1.200 -extension Stack {
   1.201 -
   1.202 -    static private var defaultManagedObjectModel: NSManagedObjectModel {
   1.203 -        let messageModelBundle = Bundle(for: self)
   1.204 -        let modelURL = messageModelBundle.url(forResource: "MessageModel", withExtension: "momd")!
   1.205 -        let objectModel = NSManagedObjectModel(contentsOf: modelURL)!
   1.206 -        return objectModel
   1.207 -    }
   1.208 -
   1.209 -    // MARK: - Defaults
   1.210 -
   1.211 -    static private var defaultName: String {
   1.212 -        guard let bundleId = Bundle.main.bundleIdentifier else {
   1.213 -            /// There is nothing we can do.
   1.214 -            fatalError()
   1.215 -        }
   1.216 -        return bundleId
   1.217 -    }
   1.218 -
   1.219 -
   1.220 -    static private var defaultURL: URL {
   1.221 -        return storeURL(for: defaultName)
   1.222 -    }
   1.223 -
   1.224 -    static private var defaultDirectory: FileManager.SearchPathDirectory {
   1.225 -        #if os(tvOS)
   1.226 -        return .cachesDirectory
   1.227 -        #else
   1.228 -        return .documentDirectory
   1.229 -        #endif
   1.230 -    }
   1.231 -
   1.232 -    static private var defaultOptions: [String:Bool] {
   1.233 -        return [NSMigratePersistentStoresAutomaticallyOption: true,
   1.234 -                NSInferMappingModelAutomaticallyOption: true]
   1.235 -    }
   1.236 -
   1.237 -    static private var storeWinsMergePolicy: NSMergePolicy {
   1.238 -        return NSMergePolicy(merge: NSMergePolicyType.mergeByPropertyObjectTrumpMergePolicyType)
   1.239 -    }
   1.240 -
   1.241 -    static private var objectWinsMergePolicy: NSMergePolicy {
   1.242 -        return NSMergePolicy(merge: NSMergePolicyType.mergeByPropertyStoreTrumpMergePolicyType)
   1.243 -    }
   1.244 -}
   1.245 -
   1.246 -// MARK: - Saving
   1.247 -
   1.248 -extension Stack {
   1.249 -
   1.250 -    func saveMainContextStateToDisk() {
   1.251 -        DispatchQueue.main.async {
   1.252 -            Stack.shared.mainContext.saveAndLogErrors()
   1.253 -        }
   1.254 -    }
   1.255 -}
   1.256 -
   1.257 -/// - note: This is here in the same file to be able to make certain context(s) fileprivate
   1.258 -extension NSManagedObjectContext {
   1.259 -
   1.260 -    /// Commits the context it's called on and is respüonsible for propagating changes to all other
   1.261 -    /// contexts.
   1.262 -    /// - note: the calling client is responsible that it is safe to call `save()` here.
   1.263 -    ///         In other works the client MUST wrap non-main context in a `perform[AndWait]` block.
   1.264 -    public func saveAndLogErrors() {
   1.265 -        if !hasChanges {
   1.266 -            return
   1.267 -        }
   1.268 -        do {
   1.269 -            // We save the moc. The client is responsible to call that the correct way.
   1.270 -            try save()
   1.271 -            if self == Stack.shared.mainContext {
   1.272 -                // We are saving the main moc.
   1.273 -                // Also save changePropagatorContext moc to propagate changes to
   1.274 -                // childs (private mocs)
   1.275 -                var resultError: Error? = nil
   1.276 -                Stack.shared.changePropagatorContext.perform {
   1.277 -                    do {
   1.278 -                        try Stack.shared.changePropagatorContext.save()
   1.279 -                    } catch {
   1.280 -                        resultError = error
   1.281 -                    }
   1.282 -                }
   1.283 -                if let er = resultError {
   1.284 -                    throw(er)
   1.285 -                }
   1.286 -            } else if let parentContext = parent {
   1.287 -                if parentContext == Stack.shared.mainContext {
   1.288 -                    // We are saving the changePropagatorContext moc.
   1.289 -                    // Do not save main from backgound contexts.
   1.290 -                } else {
   1.291 -                    // We are saving a private moc. We want to also save the parent
   1.292 -                    // (changePropagatorContext) moc too to propagate changes to main- and to the
   1.293 -                    // other private mocs.
   1.294 -                    parentContext.performAndWait {
   1.295 -                        parentContext.saveAndLogErrors()
   1.296 -                    }
   1.297 -                }
   1.298 -            }
   1.299 -        } catch {
   1.300 -            Log.shared.errorAndCrash(error: error)
   1.301 -        }
   1.302 -    }
   1.303 -}
   1.304 -*/
   1.305 -
   1.306 -// old
   1.307 -
   1.308  //
   1.309  // Stack.swift
   1.310  //
   1.311 @@ -454,7 +150,7 @@
   1.312  }
   1.313  
   1.314  // MARK: - Tear Down
   1.315 -//!!!: move to test MM target after pEp4IOS stopped using CD
   1.316 +//!!!: move to test MM target somehow after pEp4IOS stopped using CD
   1.317  extension Stack {
   1.318  
   1.319      /// Resets the Stack.
   1.320 @@ -483,8 +179,6 @@
   1.321      }
   1.322  }
   1.323  
   1.324 -//!!!!: move! Or better: RE-move
   1.325 -
   1.326  extension Stack {
   1.327  
   1.328      func execute<T: NSManagedObject>(fetchRequest request: NSFetchRequest<T>,