mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-03-30 22:31:59 +02:00
WIP: clean up persistent store setup
This commit is contained in:
@@ -282,6 +282,10 @@
|
|||||||
B5FE4DAD1C85D44E00FA6A91 /* SQLiteStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DAB1C85D44E00FA6A91 /* SQLiteStore.swift */; };
|
B5FE4DAD1C85D44E00FA6A91 /* SQLiteStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DAB1C85D44E00FA6A91 /* SQLiteStore.swift */; };
|
||||||
B5FE4DAE1C85D44E00FA6A91 /* SQLiteStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DAB1C85D44E00FA6A91 /* SQLiteStore.swift */; };
|
B5FE4DAE1C85D44E00FA6A91 /* SQLiteStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DAB1C85D44E00FA6A91 /* SQLiteStore.swift */; };
|
||||||
B5FE4DAF1C85D44E00FA6A91 /* SQLiteStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DAB1C85D44E00FA6A91 /* SQLiteStore.swift */; };
|
B5FE4DAF1C85D44E00FA6A91 /* SQLiteStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DAB1C85D44E00FA6A91 /* SQLiteStore.swift */; };
|
||||||
|
B5FEC18E1C9166E200532541 /* NSPersistentStore+Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FEC18D1C9166E200532541 /* NSPersistentStore+Setup.swift */; };
|
||||||
|
B5FEC18F1C9166E600532541 /* NSPersistentStore+Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FEC18D1C9166E200532541 /* NSPersistentStore+Setup.swift */; };
|
||||||
|
B5FEC1901C9166E700532541 /* NSPersistentStore+Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FEC18D1C9166E200532541 /* NSPersistentStore+Setup.swift */; };
|
||||||
|
B5FEC1911C9166E700532541 /* NSPersistentStore+Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FEC18D1C9166E200532541 /* NSPersistentStore+Setup.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@@ -403,6 +407,7 @@
|
|||||||
B5FE4DA11C8481E100FA6A91 /* StorageInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StorageInterface.swift; sourceTree = "<group>"; };
|
B5FE4DA11C8481E100FA6A91 /* StorageInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StorageInterface.swift; sourceTree = "<group>"; };
|
||||||
B5FE4DA61C84FB4400FA6A91 /* InMemoryStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InMemoryStore.swift; sourceTree = "<group>"; };
|
B5FE4DA61C84FB4400FA6A91 /* InMemoryStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InMemoryStore.swift; sourceTree = "<group>"; };
|
||||||
B5FE4DAB1C85D44E00FA6A91 /* SQLiteStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteStore.swift; sourceTree = "<group>"; };
|
B5FE4DAB1C85D44E00FA6A91 /* SQLiteStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteStore.swift; sourceTree = "<group>"; };
|
||||||
|
B5FEC18D1C9166E200532541 /* NSPersistentStore+Setup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSPersistentStore+Setup.swift"; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -696,17 +701,18 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
B5E84F2A1AFF849C0064E85B /* AssociatedObjects.swift */,
|
B5E84F2A1AFF849C0064E85B /* AssociatedObjects.swift */,
|
||||||
|
B5C976E61C6E3A5900B1AF90 /* CoreStoreFetchedResultsController.swift */,
|
||||||
B54A6A541BA15F2A007870FD /* FetchedResultsControllerDelegate.swift */,
|
B54A6A541BA15F2A007870FD /* FetchedResultsControllerDelegate.swift */,
|
||||||
B5E834BA1B7691F3001D3D50 /* Functions.swift */,
|
B5E834BA1B7691F3001D3D50 /* Functions.swift */,
|
||||||
B5FAD6AB1B51285300714891 /* MigrationManager.swift */,
|
B5FAD6AB1B51285300714891 /* MigrationManager.swift */,
|
||||||
B5E84F2B1AFF849C0064E85B /* NotificationObserver.swift */,
|
B5E84F2B1AFF849C0064E85B /* NotificationObserver.swift */,
|
||||||
B59D5C211B5BA34B00453479 /* NSFileManager+Setup.swift */,
|
B59D5C211B5BA34B00453479 /* NSFileManager+Setup.swift */,
|
||||||
B5C976E61C6E3A5900B1AF90 /* CoreStoreFetchedResultsController.swift */,
|
|
||||||
B5E84F2C1AFF849C0064E85B /* NSManagedObjectContext+CoreStore.swift */,
|
B5E84F2C1AFF849C0064E85B /* NSManagedObjectContext+CoreStore.swift */,
|
||||||
B5E84F351AFF85470064E85B /* NSManagedObjectContext+Querying.swift */,
|
B5E84F351AFF85470064E85B /* NSManagedObjectContext+Querying.swift */,
|
||||||
B5E84F321AFF85470064E85B /* NSManagedObjectContext+Setup.swift */,
|
B5E84F321AFF85470064E85B /* NSManagedObjectContext+Setup.swift */,
|
||||||
B5E84F331AFF85470064E85B /* NSManagedObjectContext+Transaction.swift */,
|
B5E84F331AFF85470064E85B /* NSManagedObjectContext+Transaction.swift */,
|
||||||
B51BE0691B47FC4B0069F532 /* NSManagedObjectModel+Setup.swift */,
|
B51BE0691B47FC4B0069F532 /* NSManagedObjectModel+Setup.swift */,
|
||||||
|
B5FEC18D1C9166E200532541 /* NSPersistentStore+Setup.swift */,
|
||||||
B59AFF401C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift */,
|
B59AFF401C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift */,
|
||||||
B5E84F2D1AFF849C0064E85B /* WeakObject.swift */,
|
B5E84F2D1AFF849C0064E85B /* WeakObject.swift */,
|
||||||
);
|
);
|
||||||
@@ -1037,6 +1043,7 @@
|
|||||||
B5E84F0D1AFF847B0064E85B /* BaseDataTransaction+Querying.swift in Sources */,
|
B5E84F0D1AFF847B0064E85B /* BaseDataTransaction+Querying.swift in Sources */,
|
||||||
B5FAD6AC1B51285300714891 /* MigrationManager.swift in Sources */,
|
B5FAD6AC1B51285300714891 /* MigrationManager.swift in Sources */,
|
||||||
B5E84EF61AFF846E0064E85B /* DataStack+Transaction.swift in Sources */,
|
B5E84EF61AFF846E0064E85B /* DataStack+Transaction.swift in Sources */,
|
||||||
|
B5FEC18E1C9166E200532541 /* NSPersistentStore+Setup.swift in Sources */,
|
||||||
B5E84EDF1AFF84500064E85B /* DataStack.swift in Sources */,
|
B5E84EDF1AFF84500064E85B /* DataStack.swift in Sources */,
|
||||||
B59AFF411C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift in Sources */,
|
B59AFF411C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift in Sources */,
|
||||||
B5E834BB1B7691F3001D3D50 /* Functions.swift in Sources */,
|
B5E834BB1B7691F3001D3D50 /* Functions.swift in Sources */,
|
||||||
@@ -1119,6 +1126,7 @@
|
|||||||
82BA18C61C4BBD5900A0916E /* DataStack+Migration.swift in Sources */,
|
82BA18C61C4BBD5900A0916E /* DataStack+Migration.swift in Sources */,
|
||||||
82BA18CD1C4BBD7100A0916E /* AssociatedObjects.swift in Sources */,
|
82BA18CD1C4BBD7100A0916E /* AssociatedObjects.swift in Sources */,
|
||||||
B59851491C90289D00C99590 /* NSPersistentStoreCoordinator+Setup.swift in Sources */,
|
B59851491C90289D00C99590 /* NSPersistentStoreCoordinator+Setup.swift in Sources */,
|
||||||
|
B5FEC18F1C9166E600532541 /* NSPersistentStore+Setup.swift in Sources */,
|
||||||
82BA18B71C4BBD3F00A0916E /* CoreStore+Querying.swift in Sources */,
|
82BA18B71C4BBD3F00A0916E /* CoreStore+Querying.swift in Sources */,
|
||||||
82BA18A41C4BBD2200A0916E /* SetupResult.swift in Sources */,
|
82BA18A41C4BBD2200A0916E /* SetupResult.swift in Sources */,
|
||||||
82BA18AA1C4BBD3100A0916E /* BaseDataTransaction.swift in Sources */,
|
82BA18AA1C4BBD3100A0916E /* BaseDataTransaction.swift in Sources */,
|
||||||
@@ -1169,6 +1177,7 @@
|
|||||||
B52DD1BE1BE1F94300949AFE /* NSProgress+Convenience.swift in Sources */,
|
B52DD1BE1BE1F94300949AFE /* NSProgress+Convenience.swift in Sources */,
|
||||||
B52DD1951BE1F92500949AFE /* NSError+CoreStore.swift in Sources */,
|
B52DD1951BE1F92500949AFE /* NSError+CoreStore.swift in Sources */,
|
||||||
B52DD1C21BE1F94600949AFE /* MigrationManager.swift in Sources */,
|
B52DD1C21BE1F94600949AFE /* MigrationManager.swift in Sources */,
|
||||||
|
B5FEC1911C9166E700532541 /* NSPersistentStore+Setup.swift in Sources */,
|
||||||
B52DD1AB1BE1F93900949AFE /* From.swift in Sources */,
|
B52DD1AB1BE1F93900949AFE /* From.swift in Sources */,
|
||||||
B52DD1BF1BE1F94600949AFE /* AssociatedObjects.swift in Sources */,
|
B52DD1BF1BE1F94600949AFE /* AssociatedObjects.swift in Sources */,
|
||||||
B52DD1A11BE1F92C00949AFE /* DataStack+Transaction.swift in Sources */,
|
B52DD1A11BE1F92C00949AFE /* DataStack+Transaction.swift in Sources */,
|
||||||
@@ -1272,6 +1281,7 @@
|
|||||||
B56321931BD65216006C9394 /* DataStack+Querying.swift in Sources */,
|
B56321931BD65216006C9394 /* DataStack+Querying.swift in Sources */,
|
||||||
B56321A71BD65216006C9394 /* MigrationResult.swift in Sources */,
|
B56321A71BD65216006C9394 /* MigrationResult.swift in Sources */,
|
||||||
B598514A1C90289E00C99590 /* NSPersistentStoreCoordinator+Setup.swift in Sources */,
|
B598514A1C90289E00C99590 /* NSPersistentStoreCoordinator+Setup.swift in Sources */,
|
||||||
|
B5FEC1901C9166E700532541 /* NSPersistentStore+Setup.swift in Sources */,
|
||||||
B56321A11BD65216006C9394 /* ListMonitor.swift in Sources */,
|
B56321A11BD65216006C9394 /* ListMonitor.swift in Sources */,
|
||||||
B56321881BD65216006C9394 /* BaseDataTransaction.swift in Sources */,
|
B56321881BD65216006C9394 /* BaseDataTransaction.swift in Sources */,
|
||||||
B56321A31BD65216006C9394 /* DataStack+Migration.swift in Sources */,
|
B56321A31BD65216006C9394 /* DataStack+Migration.swift in Sources */,
|
||||||
|
|||||||
@@ -50,22 +50,21 @@ class MigrationsDemoViewController: UIViewController {
|
|||||||
(dataStack: DataStack) -> ModelMetadata in
|
(dataStack: DataStack) -> ModelMetadata in
|
||||||
|
|
||||||
let models = self.models
|
let models = self.models
|
||||||
do {
|
let migrations = try! dataStack.requiredMigrationsForStorage(
|
||||||
|
SQLiteStore(fileName: "MigrationDemo.sqlite")
|
||||||
|
)
|
||||||
|
|
||||||
|
guard let storeVersion = migrations.first?.sourceVersion else {
|
||||||
|
|
||||||
|
return models.first!
|
||||||
|
}
|
||||||
|
for model in models {
|
||||||
|
|
||||||
let migrations = try dataStack.requiredMigrationsForStorage(
|
if model.version == storeVersion {
|
||||||
SQLiteStore(fileName: "MigrationDemo.sqlite")
|
|
||||||
)
|
|
||||||
|
|
||||||
let storeVersion = migrations.first?.sourceVersion ?? dataStack.modelVersion
|
|
||||||
for model in models {
|
|
||||||
|
|
||||||
if model.version == storeVersion {
|
return model
|
||||||
|
|
||||||
return model
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch _ { }
|
|
||||||
|
|
||||||
return models.first!
|
return models.first!
|
||||||
}
|
}
|
||||||
|
|||||||
78
Sources/Internal/NSPersistentStore+Setup.swift
Normal file
78
Sources/Internal/NSPersistentStore+Setup.swift
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
//
|
||||||
|
// NSPersistentStore+Setup.swift
|
||||||
|
// CoreStore
|
||||||
|
//
|
||||||
|
// Copyright © 2016 John Rommel Estropia
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import CoreData
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - NSPersistentStore
|
||||||
|
|
||||||
|
internal extension NSPersistentStore {
|
||||||
|
|
||||||
|
// MARK: Internal
|
||||||
|
|
||||||
|
@nonobjc internal var storageInterface: StorageInterface? {
|
||||||
|
|
||||||
|
get {
|
||||||
|
|
||||||
|
let wrapper: StorageObject? = getAssociatedObjectForKey(
|
||||||
|
&PropertyKeys.storageInterface,
|
||||||
|
inObject: self
|
||||||
|
)
|
||||||
|
return wrapper?.storageInterface
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
|
||||||
|
setAssociatedRetainedObject(
|
||||||
|
StorageObject(newValue),
|
||||||
|
forKey: &PropertyKeys.storageInterface,
|
||||||
|
inObject: self
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Private
|
||||||
|
|
||||||
|
private struct PropertyKeys {
|
||||||
|
|
||||||
|
static var storageInterface: Void?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - StorageObject
|
||||||
|
|
||||||
|
private class StorageObject: NSObject {
|
||||||
|
|
||||||
|
// MARK: Private
|
||||||
|
|
||||||
|
private let storageInterface: StorageInterface?
|
||||||
|
|
||||||
|
private init(_ storage: StorageInterface?) {
|
||||||
|
|
||||||
|
self.storageInterface = storage
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,39 +41,20 @@ public extension DataStack {
|
|||||||
|
|
||||||
public func addStorage<T: StorageInterface>(storage: T, completion: (SetupResult<T>) -> Void) throws -> NSProgress? {
|
public func addStorage<T: StorageInterface>(storage: T, completion: (SetupResult<T>) -> Void) throws -> NSProgress? {
|
||||||
|
|
||||||
// TODO: check
|
self.coordinator.performBlock {
|
||||||
let coordinator = self.coordinator;
|
|
||||||
// if let persistentStore = coordinator.persistentStoreForURL(fileURL) {
|
if let _ = self.persistentStoreForStorage(storage) {
|
||||||
//
|
|
||||||
// guard persistentStore.type == storage.dynamicType.storeType
|
GCDQueue.Main.async {
|
||||||
// && persistentStore.configurationName == (storage.configuration ?? Into.defaultConfigurationName) else {
|
|
||||||
//
|
completion(SetupResult(storage))
|
||||||
// let error = NSError(coreStoreErrorCode: .DifferentPersistentStoreExistsAtURL)
|
}
|
||||||
// CoreStore.handleError(
|
return
|
||||||
// error,
|
}
|
||||||
// "Failed to add SQLite \(typeName(storage)) at \"\(fileURL)\" because a different \(typeName(NSPersistentStore)) at that URL already exists."
|
|
||||||
// )
|
|
||||||
// throw error
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// GCDQueue.Main.async {
|
|
||||||
//
|
|
||||||
// completion(SetupResult(storage))
|
|
||||||
// }
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
coordinator.performBlock {
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let persistentStore = try coordinator.addPersistentStoreWithType(
|
try self.createPersistentStoreFromStorage(storage, finalURL: nil)
|
||||||
storage.dynamicType.storeType,
|
|
||||||
configuration: storage.configuration,
|
|
||||||
URL: nil,
|
|
||||||
options: storage.storeOptions
|
|
||||||
)
|
|
||||||
self.updateMetadataForStorage(storage, persistentStore: persistentStore)
|
|
||||||
|
|
||||||
GCDQueue.Main.async {
|
GCDQueue.Main.async {
|
||||||
|
|
||||||
@@ -121,105 +102,113 @@ public extension DataStack {
|
|||||||
"The specified URL for the \(typeName(storage)) is invalid: \"\(fileURL)\""
|
"The specified URL for the \(typeName(storage)) is invalid: \"\(fileURL)\""
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: check
|
return try self.coordinator.performBlockAndWait {
|
||||||
let coordinator = self.coordinator;
|
|
||||||
if let persistentStore = coordinator.persistentStoreForURL(fileURL) {
|
|
||||||
|
|
||||||
guard persistentStore.type == storage.dynamicType.storeType
|
if let _ = self.persistentStoreForStorage(storage) {
|
||||||
&& persistentStore.configurationName == (storage.configuration ?? Into.defaultConfigurationName) else {
|
|
||||||
|
|
||||||
let error = NSError(coreStoreErrorCode: .DifferentPersistentStoreExistsAtURL)
|
|
||||||
CoreStore.handleError(
|
|
||||||
error,
|
|
||||||
"Failed to add \(typeName(storage)) at \"\(fileURL)\" because a different \(typeName(NSPersistentStore)) at that URL already exists."
|
|
||||||
)
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
|
|
||||||
GCDQueue.Main.async {
|
|
||||||
|
|
||||||
completion(SetupResult(storage))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let fileManager = NSFileManager.defaultManager()
|
|
||||||
|
|
||||||
do {
|
|
||||||
|
|
||||||
try fileManager.createDirectoryAtURL(
|
|
||||||
fileURL.URLByDeletingLastPathComponent!,
|
|
||||||
withIntermediateDirectories: true,
|
|
||||||
attributes: nil
|
|
||||||
)
|
|
||||||
|
|
||||||
let metadata = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(
|
|
||||||
storage.dynamicType.storeType,
|
|
||||||
URL: fileURL,
|
|
||||||
options: storage.storeOptions
|
|
||||||
)
|
|
||||||
|
|
||||||
return self.upgradeStorageIfNeeded(
|
|
||||||
storage,
|
|
||||||
metadata: metadata,
|
|
||||||
completion: { (result) -> Void in
|
|
||||||
|
|
||||||
if case .Failure(let error) = result {
|
|
||||||
|
|
||||||
if storage.resetStoreOnModelMismatch && error.isCoreDataMigrationError {
|
|
||||||
|
|
||||||
do {
|
|
||||||
|
|
||||||
try _ = self.model[metadata].flatMap(storage.eraseStorageAndWait)
|
|
||||||
try self.addStorageAndWait(storage)
|
|
||||||
|
|
||||||
GCDQueue.Main.async {
|
|
||||||
|
|
||||||
completion(SetupResult(storage))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
|
|
||||||
completion(SetupResult(error as NSError))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
completion(SetupResult(error))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
|
|
||||||
try self.addStorageAndWait(storage)
|
|
||||||
|
|
||||||
completion(SetupResult(storage))
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
|
|
||||||
completion(SetupResult(error as NSError))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
catch let error as NSError
|
|
||||||
where error.code == NSFileReadNoSuchFileError && error.domain == NSCocoaErrorDomain {
|
|
||||||
|
|
||||||
try self.addStorageAndWait(storage)
|
|
||||||
|
|
||||||
GCDQueue.Main.async {
|
GCDQueue.Main.async {
|
||||||
|
|
||||||
completion(SetupResult(storage))
|
completion(SetupResult(storage))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
catch {
|
|
||||||
|
|
||||||
CoreStore.handleError(
|
if let persistentStore = self.coordinator.persistentStoreForURL(fileURL) {
|
||||||
error as NSError,
|
|
||||||
"Failed to load SQLite \(typeName(NSPersistentStore)) metadata."
|
if let existingStorage = persistentStore.storageInterface as? T
|
||||||
)
|
where storage.matchesPersistentStore(persistentStore) {
|
||||||
throw error
|
|
||||||
|
GCDQueue.Main.async {
|
||||||
|
|
||||||
|
completion(SetupResult(existingStorage))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let error = NSError(coreStoreErrorCode: .DifferentPersistentStoreExistsAtURL)
|
||||||
|
CoreStore.handleError(
|
||||||
|
error,
|
||||||
|
"Failed to add \(typeName(storage)) at \"\(fileURL)\" because a different \(typeName(NSPersistentStore)) at that URL already exists."
|
||||||
|
)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
try NSFileManager.defaultManager().createDirectoryAtURL(
|
||||||
|
fileURL.URLByDeletingLastPathComponent!,
|
||||||
|
withIntermediateDirectories: true,
|
||||||
|
attributes: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
let metadata = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(
|
||||||
|
storage.dynamicType.storeType,
|
||||||
|
URL: fileURL,
|
||||||
|
options: storage.storeOptions
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.upgradeStorageIfNeeded(
|
||||||
|
storage,
|
||||||
|
metadata: metadata,
|
||||||
|
completion: { (result) -> Void in
|
||||||
|
|
||||||
|
if case .Failure(let error) = result {
|
||||||
|
|
||||||
|
if storage.resetStoreOnModelMismatch && error.isCoreDataMigrationError {
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
try _ = self.model[metadata].flatMap(storage.eraseStorageAndWait)
|
||||||
|
try self.addStorageAndWait(storage)
|
||||||
|
|
||||||
|
GCDQueue.Main.async {
|
||||||
|
|
||||||
|
completion(SetupResult(storage))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
|
||||||
|
completion(SetupResult(error as NSError))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
completion(SetupResult(error))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
try self.addStorageAndWait(storage)
|
||||||
|
|
||||||
|
completion(SetupResult(storage))
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
|
||||||
|
completion(SetupResult(error as NSError))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
catch let error as NSError
|
||||||
|
where error.code == NSFileReadNoSuchFileError && error.domain == NSCocoaErrorDomain {
|
||||||
|
|
||||||
|
try self.addStorageAndWait(storage)
|
||||||
|
|
||||||
|
GCDQueue.Main.async {
|
||||||
|
|
||||||
|
completion(SetupResult(storage))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
|
||||||
|
CoreStore.handleError(
|
||||||
|
error as NSError,
|
||||||
|
"Failed to load SQLite \(typeName(NSPersistentStore)) metadata."
|
||||||
|
)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,30 +223,36 @@ public extension DataStack {
|
|||||||
*/
|
*/
|
||||||
public func upgradeStorageIfNeeded<T: LocalStorage>(storage: T, completion: (MigrationResult) -> Void) throws -> NSProgress? {
|
public func upgradeStorageIfNeeded<T: LocalStorage>(storage: T, completion: (MigrationResult) -> Void) throws -> NSProgress? {
|
||||||
|
|
||||||
let fileURL = storage.fileURL
|
return try self.coordinator.performBlockAndWait {
|
||||||
let metadata: [String: AnyObject]
|
|
||||||
do {
|
|
||||||
|
|
||||||
metadata = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(
|
let fileURL = storage.fileURL
|
||||||
storage.dynamicType.storeType,
|
do {
|
||||||
URL: fileURL,
|
|
||||||
options: storage.storeOptions
|
CoreStore.assert(
|
||||||
)
|
self.persistentStoreForStorage(storage) == nil,
|
||||||
|
"Attempted to migrate an already added \(typeName(storage)) at URL \"\(fileURL)\""
|
||||||
|
)
|
||||||
|
|
||||||
|
let metadata = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(
|
||||||
|
storage.dynamicType.storeType,
|
||||||
|
URL: fileURL,
|
||||||
|
options: storage.storeOptions
|
||||||
|
)
|
||||||
|
return self.upgradeStorageIfNeeded(
|
||||||
|
storage,
|
||||||
|
metadata: metadata,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
|
||||||
|
CoreStore.handleError(
|
||||||
|
error as NSError,
|
||||||
|
"Failed to load \(typeName(storage)) metadata from URL \"\(fileURL)\"."
|
||||||
|
)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch {
|
|
||||||
|
|
||||||
CoreStore.handleError(
|
|
||||||
error as NSError,
|
|
||||||
"Failed to load \(typeName(storage)) metadata from URL \"\(fileURL)\"."
|
|
||||||
)
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.upgradeStorageIfNeeded(
|
|
||||||
storage,
|
|
||||||
metadata: metadata,
|
|
||||||
completion: completion
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -272,36 +267,48 @@ public extension DataStack {
|
|||||||
@warn_unused_result
|
@warn_unused_result
|
||||||
public func requiredMigrationsForStorage<T: LocalStorage>(storage: T) throws -> [MigrationType] {
|
public func requiredMigrationsForStorage<T: LocalStorage>(storage: T) throws -> [MigrationType] {
|
||||||
|
|
||||||
let fileURL = storage.fileURL
|
return try self.coordinator.performBlockAndWait {
|
||||||
let metadata: [String : AnyObject]
|
|
||||||
do {
|
|
||||||
|
|
||||||
metadata = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(
|
let fileURL = storage.fileURL
|
||||||
storage.dynamicType.storeType,
|
|
||||||
URL: fileURL,
|
|
||||||
options: storage.storeOptions
|
|
||||||
)
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
|
|
||||||
CoreStore.handleError(
|
CoreStore.assert(
|
||||||
error as NSError,
|
self.persistentStoreForStorage(storage) == nil,
|
||||||
"Failed to load \(typeName(storage)) metadata from URL \"\(fileURL)\"."
|
"Attempted to query required migrations for an already added \(typeName(storage)) at URL \"\(fileURL)\""
|
||||||
)
|
)
|
||||||
throw error
|
do {
|
||||||
|
|
||||||
|
let metadata = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(
|
||||||
|
storage.dynamicType.storeType,
|
||||||
|
URL: fileURL,
|
||||||
|
options: storage.storeOptions
|
||||||
|
)
|
||||||
|
|
||||||
|
guard let migrationSteps = self.computeMigrationFromStorageMetadata(metadata, configuration: storage.configuration, mappingModelBundles: storage.mappingModelBundles) else {
|
||||||
|
|
||||||
|
let error = NSError(coreStoreErrorCode: .MappingModelNotFound)
|
||||||
|
CoreStore.handleError(
|
||||||
|
error,
|
||||||
|
"Failed to find migration steps from the \(typeName(storage)) at URL \"\(fileURL)\" to version model \"\(self.modelVersion)\"."
|
||||||
|
)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
|
return migrationSteps.map { $0.migrationType }
|
||||||
|
}
|
||||||
|
catch let error as NSError
|
||||||
|
where error.code == NSFileReadNoSuchFileError && error.domain == NSCocoaErrorDomain {
|
||||||
|
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
|
||||||
|
CoreStore.handleError(
|
||||||
|
error as NSError,
|
||||||
|
"Failed to load \(typeName(storage)) metadata from URL \"\(fileURL)\"."
|
||||||
|
)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let migrationSteps = self.computeMigrationFromStorageMetadata(metadata, configuration: storage.configuration, mappingModelBundles: storage.mappingModelBundles) else {
|
|
||||||
|
|
||||||
let error = NSError(coreStoreErrorCode: .MappingModelNotFound)
|
|
||||||
CoreStore.handleError(
|
|
||||||
error,
|
|
||||||
"Failed to find migration steps from the \(typeName(storage)) at URL \"\(fileURL)\" to version model \"\(self.modelVersion)\"."
|
|
||||||
)
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
|
|
||||||
return migrationSteps.map { $0.migrationType }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ public extension CoreStore {
|
|||||||
```
|
```
|
||||||
|
|
||||||
- parameter storage: the local storage
|
- parameter storage: the local storage
|
||||||
- returns: the local storage added to the `defaultStack`
|
- returns: the local storage added to the `defaultStack`. Note that this may not always be the same instance as the parameter argument if a previous `LocalStorage` was already added at the same URL and with the same configuration.
|
||||||
*/
|
*/
|
||||||
public static func addStorageAndWait<T: LocalStorage>(storage: T) throws -> T {
|
public static func addStorageAndWait<T: LocalStorage>(storage: T) throws -> T {
|
||||||
|
|
||||||
|
|||||||
@@ -149,21 +149,18 @@ public final class DataStack {
|
|||||||
*/
|
*/
|
||||||
public func addStorageAndWait<T: StorageInterface>(storage: T) throws -> T {
|
public func addStorageAndWait<T: StorageInterface>(storage: T) throws -> T {
|
||||||
|
|
||||||
CoreStore.assert(
|
|
||||||
storage.internalStore == nil,
|
|
||||||
"The specified \"\(typeName(storage))\" was already added to the data stack: \(storage)"
|
|
||||||
)
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let persistentStore = try self.coordinator.addPersistentStoreSynchronously(
|
return try self.coordinator.performBlockAndWait { () -> T in
|
||||||
storage.dynamicType.storeType,
|
|
||||||
configuration: storage.configuration,
|
if let _ = self.persistentStoreForStorage(storage) {
|
||||||
URL: nil,
|
|
||||||
options: storage.storeOptions
|
return storage
|
||||||
)
|
}
|
||||||
self.updateMetadataForStorage(storage, persistentStore: persistentStore)
|
|
||||||
return storage
|
try self.createPersistentStoreFromStorage(storage, finalURL: nil)
|
||||||
|
return storage
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
|
|
||||||
@@ -196,64 +193,50 @@ public final class DataStack {
|
|||||||
```
|
```
|
||||||
|
|
||||||
- parameter storage: the local storage
|
- parameter storage: the local storage
|
||||||
- returns: the local storage added to the stack
|
- returns: the local storage added to the stack. Note that this may not always be the same instance as the parameter argument if a previous `LocalStorage` was already added at the same URL and with the same configuration.
|
||||||
*/
|
*/
|
||||||
public func addStorageAndWait<T: LocalStorage>(storage: T) throws -> T {
|
public func addStorageAndWait<T: LocalStorage>(storage: T) throws -> T {
|
||||||
|
|
||||||
CoreStore.assert(
|
return try self.coordinator.performBlockAndWait {
|
||||||
storage.internalStore == nil,
|
|
||||||
"The specified \"\(typeName(storage))\" was already added to the data stack: \(storage)"
|
let fileURL = storage.fileURL
|
||||||
)
|
CoreStore.assert(
|
||||||
|
fileURL.fileURL,
|
||||||
let fileURL = storage.fileURL
|
"The specified store URL for the \"\(typeName(storage))\" is invalid: \"\(fileURL)\""
|
||||||
CoreStore.assert(
|
)
|
||||||
fileURL.fileURL,
|
|
||||||
"The specified store URL for the \"\(typeName(storage))\" is invalid: \"\(fileURL)\""
|
if let _ = self.persistentStoreForStorage(storage) {
|
||||||
)
|
|
||||||
|
return storage
|
||||||
if let persistentStore = coordinator.persistentStoreForURL(fileURL) {
|
|
||||||
|
|
||||||
guard persistentStore.type == storage.dynamicType.storeType
|
|
||||||
&& persistentStore.configurationName == (storage.configuration ?? Into.defaultConfigurationName) else {
|
|
||||||
|
|
||||||
let error = NSError(coreStoreErrorCode: .DifferentPersistentStoreExistsAtURL)
|
|
||||||
CoreStore.handleError(
|
|
||||||
error,
|
|
||||||
"Failed to add \(typeName(storage)) at \"\(fileURL)\" because a different \(typeName(NSPersistentStore)) at that URL already exists."
|
|
||||||
)
|
|
||||||
throw error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
storage.internalStore = persistentStore
|
if let persistentStore = self.coordinator.persistentStoreForURL(fileURL) {
|
||||||
return storage
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
|
|
||||||
let coordinator = self.coordinator
|
|
||||||
return try coordinator.performBlockAndWait {
|
|
||||||
|
|
||||||
let addStorage = { () throws -> T in
|
if let existingStorage = persistentStore.storageInterface as? T
|
||||||
|
where storage.matchesPersistentStore(persistentStore) {
|
||||||
|
|
||||||
let persistentStore = try coordinator.addPersistentStoreWithType(
|
return existingStorage
|
||||||
storage.dynamicType.storeType,
|
|
||||||
configuration: storage.configuration,
|
|
||||||
URL: fileURL,
|
|
||||||
options: storage.storeOptions
|
|
||||||
)
|
|
||||||
self.updateMetadataForStorage(storage, persistentStore: persistentStore)
|
|
||||||
return storage
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let fileManager = NSFileManager.defaultManager()
|
let error = NSError(coreStoreErrorCode: .DifferentPersistentStoreExistsAtURL)
|
||||||
|
CoreStore.handleError(
|
||||||
|
error,
|
||||||
|
"Failed to add \(typeName(storage)) at \"\(fileURL)\" because a different \(typeName(NSPersistentStore)) at that URL already exists."
|
||||||
|
)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
try fileManager.createDirectoryAtURL(
|
try NSFileManager.defaultManager().createDirectoryAtURL(
|
||||||
fileURL.URLByDeletingLastPathComponent!,
|
fileURL.URLByDeletingLastPathComponent!,
|
||||||
withIntermediateDirectories: true,
|
withIntermediateDirectories: true,
|
||||||
attributes: nil
|
attributes: nil
|
||||||
)
|
)
|
||||||
return try addStorage()
|
try self.createPersistentStoreFromStorage(storage, finalURL: fileURL)
|
||||||
|
return storage
|
||||||
}
|
}
|
||||||
catch let error as NSError where storage.resetStoreOnModelMismatch && error.isCoreDataMigrationError {
|
catch let error as NSError where storage.resetStoreOnModelMismatch && error.isCoreDataMigrationError {
|
||||||
|
|
||||||
@@ -264,17 +247,18 @@ public final class DataStack {
|
|||||||
)
|
)
|
||||||
try _ = self.model[metadata].flatMap(storage.eraseStorageAndWait)
|
try _ = self.model[metadata].flatMap(storage.eraseStorageAndWait)
|
||||||
|
|
||||||
return try addStorage()
|
try self.createPersistentStoreFromStorage(storage, finalURL: fileURL)
|
||||||
|
return storage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
catch {
|
||||||
catch {
|
|
||||||
|
CoreStore.handleError(
|
||||||
CoreStore.handleError(
|
error as NSError,
|
||||||
error as NSError,
|
"Failed to add \(typeName(storage)) to the stack."
|
||||||
"Failed to add \(typeName(storage)) to the stack."
|
)
|
||||||
)
|
throw error
|
||||||
throw error
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,6 +284,13 @@ public final class DataStack {
|
|||||||
return migrationQueue
|
return migrationQueue
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
internal func persistentStoreForStorage(storage: StorageInterface) -> NSPersistentStore? {
|
||||||
|
|
||||||
|
return self.coordinator.persistentStores
|
||||||
|
.filter { $0.storageInterface === storage }
|
||||||
|
.first
|
||||||
|
}
|
||||||
|
|
||||||
internal func entityNameForEntityClass(entityClass: AnyClass) -> String? {
|
internal func entityNameForEntityClass(entityClass: AnyClass) -> String? {
|
||||||
|
|
||||||
return self.model.entityNameForClass(entityClass)
|
return self.model.entityNameForClass(entityClass)
|
||||||
@@ -352,9 +343,15 @@ public final class DataStack {
|
|||||||
return returnValue
|
return returnValue
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func updateMetadataForStorage(storage: StorageInterface, persistentStore: NSPersistentStore) {
|
internal func createPersistentStoreFromStorage(storage: StorageInterface, finalURL: NSURL?) throws -> NSPersistentStore {
|
||||||
|
|
||||||
storage.internalStore = persistentStore
|
let persistentStore = try self.coordinator.addPersistentStoreWithType(
|
||||||
|
storage.dynamicType.storeType,
|
||||||
|
configuration: storage.configuration,
|
||||||
|
URL: finalURL,
|
||||||
|
options: storage.storeOptions
|
||||||
|
)
|
||||||
|
persistentStore.storageInterface = storage
|
||||||
|
|
||||||
self.storeMetadataUpdateQueue.barrierAsync {
|
self.storeMetadataUpdateQueue.barrierAsync {
|
||||||
|
|
||||||
@@ -375,6 +372,7 @@ public final class DataStack {
|
|||||||
self.entityConfigurationsMapping[managedObjectClassName]?.insert(configurationName)
|
self.entityConfigurationsMapping[managedObjectClassName]?.insert(configurationName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return persistentStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import CoreData
|
|||||||
|
|
||||||
// MARK: - SetupResult
|
// MARK: - SetupResult
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The `SetupResult` indicates the result of an asynchronous initialization of a persistent store.
|
The `SetupResult` indicates the result of an asynchronous initialization of a persistent store.
|
||||||
The `SetupResult` can be treated as a boolean:
|
The `SetupResult` can be treated as a boolean:
|
||||||
@@ -43,7 +42,7 @@ import CoreData
|
|||||||
else {
|
else {
|
||||||
// failed
|
// failed
|
||||||
}
|
}
|
||||||
}n
|
}
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
or as an `enum`, where the resulting associated object can also be inspected:
|
or as an `enum`, where the resulting associated object can also be inspected:
|
||||||
@@ -114,18 +113,18 @@ public enum SetupResult<T: StorageInterface>: BooleanType {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Deprecated. Replaced by `SetupResult<T>` by using the new `addStorage(_:completion:)` method variants.
|
Deprecated. Replaced by `SetupResult<T>` when using the new `addStorage(_:completion:)` method variants.
|
||||||
*/
|
*/
|
||||||
@available(*, deprecated=2.0.0, message="Replaced by SetupResult by using the new addStorage(_:completion:) method variants.")
|
@available(*, deprecated=2.0.0, message="Replaced by SetupResult by using the new addStorage(_:completion:) method variants.")
|
||||||
public enum PersistentStoreResult: BooleanType {
|
public enum PersistentStoreResult: BooleanType {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Deprecated. Replaced by `SetupResult.Success` by using the new `addStorage(_:completion:)` method variants.
|
Deprecated. Replaced by `SetupResult.Success` when using the new `addStorage(_:completion:)` method variants.
|
||||||
*/
|
*/
|
||||||
case Success(NSPersistentStore)
|
case Success(NSPersistentStore)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Deprecated. Replaced by `SetupResult.Failure` by using the new `addStorage(_:completion:)` method variants.
|
Deprecated. Replaced by `SetupResult.Failure` when using the new `addStorage(_:completion:)` method variants.
|
||||||
*/
|
*/
|
||||||
case Failure(NSError)
|
case Failure(NSError)
|
||||||
|
|
||||||
|
|||||||
@@ -34,8 +34,6 @@ public protocol StorageInterface: class {
|
|||||||
|
|
||||||
var configuration: String? { get }
|
var configuration: String? { get }
|
||||||
var storeOptions: [String: AnyObject]? { get }
|
var storeOptions: [String: AnyObject]? { get }
|
||||||
|
|
||||||
var internalStore: NSPersistentStore? { get set }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -57,3 +55,16 @@ public protocol LocalStorage: StorageInterface {
|
|||||||
|
|
||||||
func eraseStorageAndWait(soureModel soureModel: NSManagedObjectModel) throws
|
func eraseStorageAndWait(soureModel soureModel: NSManagedObjectModel) throws
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Internal
|
||||||
|
|
||||||
|
internal extension LocalStorage {
|
||||||
|
|
||||||
|
internal func matchesPersistentStore(persistentStore: NSPersistentStore) -> Bool {
|
||||||
|
|
||||||
|
return persistentStore.type == self.dynamicType.storeType
|
||||||
|
&& persistentStore.configurationName == (self.configuration ?? Into.defaultConfigurationName)
|
||||||
|
&& persistentStore.URL == self.fileURL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user