migration utilities (beta)

This commit is contained in:
John Rommel Estropia
2015-07-07 07:58:16 +09:00
parent 261c3a6001
commit bf0eebe057
7 changed files with 748 additions and 458 deletions

View File

@@ -37,13 +37,16 @@ public extension DataStack {
/**
Fetches the first `NSManagedObject` instance that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the first `NSManagedObject` instance that satisfies the specified `FetchClause`s
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the first `NSManagedObject` instance that satisfies the specified `FetchClause`s
*/
public func fetchOne<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> T? {
CoreStore.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to fetch from a \(typeName(self)) outside the main thread."
)
return self.mainContext.fetchOne(from, fetchClauses)
}
@@ -51,13 +54,16 @@ public extension DataStack {
/**
Fetches the first `NSManagedObject` instance that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the first `NSManagedObject` instance that satisfies the specified `FetchClause`s
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the first `NSManagedObject` instance that satisfies the specified `FetchClause`s
*/
public func fetchOne<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> T? {
CoreStore.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to fetch from a \(typeName(self)) outside the main thread."
)
return self.mainContext.fetchOne(from, fetchClauses)
}
@@ -65,13 +71,16 @@ public extension DataStack {
/**
Fetches all `NSManagedObject` instances that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: all `NSManagedObject` instances that satisfy the specified `FetchClause`s
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: all `NSManagedObject` instances that satisfy the specified `FetchClause`s
*/
public func fetchAll<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> [T]? {
CoreStore.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to fetch from a \(typeName(self)) outside the main thread."
)
return self.mainContext.fetchAll(from, fetchClauses)
}
@@ -79,13 +88,16 @@ public extension DataStack {
/**
Fetches all `NSManagedObject` instances that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: all `NSManagedObject` instances that satisfy the specified `FetchClause`s
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: all `NSManagedObject` instances that satisfy the specified `FetchClause`s
*/
public func fetchAll<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [T]? {
CoreStore.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to fetch from a \(typeName(self)) outside the main thread."
)
return self.mainContext.fetchAll(from, fetchClauses)
}
@@ -93,13 +105,16 @@ public extension DataStack {
/**
Fetches the number of `NSManagedObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the number `NSManagedObject`s that satisfy the specified `FetchClause`s
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the number `NSManagedObject`s that satisfy the specified `FetchClause`s
*/
public func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> Int? {
CoreStore.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to fetch from a \(typeName(self)) outside the main thread."
)
return self.mainContext.fetchCount(from, fetchClauses)
}
@@ -107,13 +122,16 @@ public extension DataStack {
/**
Fetches the number of `NSManagedObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the number `NSManagedObject`s that satisfy the specified `FetchClause`s
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the number `NSManagedObject`s that satisfy the specified `FetchClause`s
*/
public func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> Int? {
CoreStore.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to fetch from a \(typeName(self)) outside the main thread."
)
return self.mainContext.fetchCount(from, fetchClauses)
}
@@ -121,13 +139,16 @@ public extension DataStack {
/**
Fetches the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s
*/
public func fetchObjectID<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> NSManagedObjectID? {
CoreStore.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to fetch from a \(typeName(self)) outside the main thread."
)
return self.mainContext.fetchObjectID(from, fetchClauses)
}
@@ -135,13 +156,16 @@ public extension DataStack {
/**
Fetches the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s
*/
public func fetchObjectID<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? {
CoreStore.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to fetch from a \(typeName(self)) outside the main thread."
)
return self.mainContext.fetchObjectID(from, fetchClauses)
}
@@ -149,13 +173,16 @@ public extension DataStack {
/**
Fetches the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s
*/
public func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? {
CoreStore.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to fetch from a \(typeName(self)) outside the main thread."
)
return self.mainContext.fetchObjectIDs(from, fetchClauses)
}
@@ -163,13 +190,16 @@ public extension DataStack {
/**
Fetches the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s
*/
public func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? {
CoreStore.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to fetch from a \(typeName(self)) outside the main thread."
)
return self.mainContext.fetchObjectIDs(from, fetchClauses)
}
@@ -179,14 +209,17 @@ public extension DataStack {
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
:param: from a `From` clause indicating the entity type
:param: selectClause a `Select<U>` clause indicating the properties to fetch, and with the generic type indicating the return type.
:param: queryClauses a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
:returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
- parameter from: a `From` clause indicating the entity type
- parameter selectClause: a `Select<U>` clause indicating the properties to fetch, and with the generic type indicating the return type.
- parameter queryClauses: a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
- returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
*/
public func queryValue<T: NSManagedObject, U: SelectValueResultType>(from: From<T>, _ selectClause: Select<U>, _ queryClauses: QueryClause...) -> U? {
CoreStore.assert(NSThread.isMainThread(), "Attempted to query from a \(typeName(self)) outside the main thread.")
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to query from a \(typeName(self)) outside the main thread."
)
return self.mainContext.queryValue(from, selectClause, queryClauses)
}
@@ -196,14 +229,17 @@ public extension DataStack {
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
:param: from a `From` clause indicating the entity type
:param: selectClause a `Select<U>` clause indicating the properties to fetch, and with the generic type indicating the return type.
:param: queryClauses a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
:returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
- parameter from: a `From` clause indicating the entity type
- parameter selectClause: a `Select<U>` clause indicating the properties to fetch, and with the generic type indicating the return type.
- parameter queryClauses: a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
- returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
*/
public func queryValue<T: NSManagedObject, U: SelectValueResultType>(from: From<T>, _ selectClause: Select<U>, _ queryClauses: [QueryClause]) -> U? {
CoreStore.assert(NSThread.isMainThread(), "Attempted to query from a \(typeName(self)) outside the main thread.")
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to query from a \(typeName(self)) outside the main thread."
)
return self.mainContext.queryValue(from, selectClause, queryClauses)
}
@@ -213,14 +249,17 @@ public extension DataStack {
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
:param: from a `From` clause indicating the entity type
:param: selectClause a `Select<U>` clause indicating the properties to fetch, and with the generic type indicating the return type.
:param: queryClauses a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
:returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
- parameter from: a `From` clause indicating the entity type
- parameter selectClause: a `Select<U>` clause indicating the properties to fetch, and with the generic type indicating the return type.
- parameter queryClauses: a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
- returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
*/
public func queryAttributes<T: NSManagedObject>(from: From<T>, _ selectClause: Select<NSDictionary>, _ queryClauses: QueryClause...) -> [[NSString: AnyObject]]? {
CoreStore.assert(NSThread.isMainThread(), "Attempted to query from a \(typeName(self)) outside the main thread.")
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to query from a \(typeName(self)) outside the main thread."
)
return self.mainContext.queryAttributes(from, selectClause, queryClauses)
}
@@ -230,14 +269,17 @@ public extension DataStack {
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
:param: from a `From` clause indicating the entity type
:param: selectClause a `Select<U>` clause indicating the properties to fetch, and with the generic type indicating the return type.
:param: queryClauses a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
:returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
- parameter from: a `From` clause indicating the entity type
- parameter selectClause: a `Select<U>` clause indicating the properties to fetch, and with the generic type indicating the return type.
- parameter queryClauses: a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
- returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
*/
public func queryAttributes<T: NSManagedObject>(from: From<T>, _ selectClause: Select<NSDictionary>, _ queryClauses: [QueryClause]) -> [[NSString: AnyObject]]? {
CoreStore.assert(NSThread.isMainThread(), "Attempted to query from a \(typeName(self)) outside the main thread.")
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to query from a \(typeName(self)) outside the main thread."
)
return self.mainContext.queryAttributes(from, selectClause, queryClauses)
}

View File

@@ -0,0 +1,99 @@
//
// NSManagedObjectModel+Setup.swift
// CoreStore
//
// Copyright (c) 2015 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: - NSManagedObjectModel
internal extension NSManagedObjectModel {
// MARK: Internal
func entityNameForClass(entityClass: AnyClass) -> String {
return self.entityNameMapping[NSStringFromClass(entityClass)]!
}
func configurationsForClass(entityClass: AnyClass) -> Set<String> {
return self.entityConfigurationsMapping[NSStringFromClass(entityClass)]!
}
// MARK: Private
internal var entityNameMapping: [String: String] {
get {
if let mapping: NSDictionary? = getAssociatedObjectForKey(&PropertyKeys.entityNameMapping, inObject: self) {
return mapping as! [String: String]
}
let mapping = self.entities.reduce([String: String]()) {
(var mapping, entityDescription) -> [String: String] in
if let entityName = entityDescription.name {
let className = entityDescription.managedObjectClassName
mapping[className] = entityName
}
return mapping
}
setAssociatedCopiedObject(
mapping as NSDictionary,
forKey: &PropertyKeys.entityNameMapping,
inObject: self
)
return mapping
}
}
private lazy var entityConfigurationsMapping: [String: Set<String>] = {
[unowned self] in
return self.configurations.reduce([String: Set<String>]()) {
(var mapping, configuration) -> [String: Set<String>] in
return (self.entitiesForConfiguration(configuration) ?? []).reduce(mapping) {
(var mapping, entityDescription) -> [String: Set<String>] in
let className = entityDescription.managedObjectClassName
mapping[className]?.insert(configuration)
return mapping
}
}
}()
private struct PropertyKeys {
static var entityNameMapping: Void?
static var entityConfigurationsMapping: Void?
}
}

View File

@@ -0,0 +1,13 @@
//
// RootManagedObjectModel.swift
// CoreStore
//
// Created by John Rommel Estropia on 2015/07/04.
// Copyright © 2015 John Rommel Estropia. All rights reserved.
//
import UIKit
class RootManagedObjectModel: NSManagedObjectModel {
}

View File

@@ -35,12 +35,12 @@ public extension DataStack {
/**
Checks if the store with the specified filename and configuration needs to be migrated to the `DataStack`'s managed object model version.
:param: fileName the local filename for the SQLite persistent store in the "Application Support" directory.
:param: configuration an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration.
:param: mappingModelBundles an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`.
- parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory.
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration.
- parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`.
:return: a `MigrationType` indicating the type of migration required for the store; or `nil` if either inspection of the store failed, or no mapping model was found/inferred. `MigrationType` acts as a `Bool` and evaluates to `false` if no migration is required, and `true` if either a lightweight or custom migration is needed.
*/
public func needsMigrationForSQLiteStore(fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as! [NSBundle]) -> MigrationType? {
public func needsMigrationForSQLiteStore(fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as [NSBundle]) -> MigrationType? {
return needsMigrationForSQLiteStore(
fileURL: applicationSupportDirectory.URLByAppendingPathComponent(
@@ -55,68 +55,72 @@ public extension DataStack {
/**
Checks if the store at the specified file URL and configuration needs to be migrated to the `DataStack`'s managed object model version.
:param: fileURL the local file URL for the SQLite persistent store.
:param: configuration an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration.
:param: mappingModelBundles an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`.
- parameter fileURL: the local file URL for the SQLite persistent store.
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration.
- parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`.
:return: a `MigrationType` indicating the type of migration required for the store; or `nil` if either inspection of the store failed, or no mapping model was found/inferred. `MigrationType` acts as a `Bool` and evaluates to `false` if no migration is required, and `true` if either a lightweight or custom migration is needed.
*/
public func needsMigrationForSQLiteStore(fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as! [NSBundle]) -> MigrationType? {
public func needsMigrationForSQLiteStore(fileURL fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as [NSBundle]) -> MigrationType? {
var error: NSError?
let metadata: [NSObject : AnyObject]! = NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(
NSSQLiteStoreType,
URL: fileURL,
error: &error
)
if metadata == nil {
let metadata: [String : AnyObject]
do {
metadata = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(
NSSQLiteStoreType,
URL: fileURL
)
}
catch {
CoreStore.handleError(
error ?? NSError(coreStoreErrorCode: .UnknownError),
"Failed to add SQLite <\(NSPersistentStore.self)> at \"\(fileURL)\"."
error as NSError,
"Failed to add SQLite \(typeName(NSPersistentStore)) at \"\(fileURL)\"."
)
return nil
}
let coordinator = self.coordinator;
let destinationModel = coordinator.managedObjectModel
if destinationModel.isConfiguration(
configuration,
compatibleWithStoreMetadata: metadata) {
return .None
if destinationModel.isConfiguration(configuration, compatibleWithStoreMetadata: metadata) {
return .None
}
let sourceModel = NSManagedObjectModel(
byMergingModels: [destinationModel],
forStoreMetadata: metadata
)!
guard let sourceModel = NSManagedObjectModel(byMergingModels: [destinationModel], forStoreMetadata: metadata) else {
return nil
}
if NSMappingModel(
if let _ = NSMappingModel(
fromBundles: mappingModelBundles,
forSourceModel: sourceModel,
destinationModel: destinationModel) != nil {
destinationModel: destinationModel) {
return .Heavyweight
}
if NSMappingModel.inferredMappingModelForSourceModel(
sourceModel,
destinationModel: destinationModel,
error: nil) != nil {
return .Lightweight
do {
try NSMappingModel.inferredMappingModelForSourceModel(
sourceModel,
destinationModel: destinationModel
)
return .Lightweight
}
catch {
return nil
}
return nil
}
/**
Migrates an SQLite store with the specified filename to the `DataStack`'s managed object model version. This method does NOT add the migrated store to the data stack.
:param: fileName the local filename for the SQLite persistent store in the "Application Support" directory.
:param: configuration an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration.
:param: sourceBundles an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`.
:param: sourceBundles an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`.
- parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory.
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration.
- parameter sourceBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`.
- parameter sourceBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`.
*/
public func upgradeSQLiteStoreIfNeeded(fileName: String, configuration: String? = nil, sourceBundles: [NSBundle]? = nil, completion: (MigrationResult) -> Void) -> MigrationType? {
@@ -134,102 +138,129 @@ public extension DataStack {
/**
Migrates an SQLite store at the specified file URL and configuration name to the `DataStack`'s managed object model version. This method does NOT add the migrated store to the data stack.
:param: fileName the local filename for the SQLite persistent store in the "Application Support" directory.
:param: configuration an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration.
:param: sourceBundles an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`.
:param: sourceBundles an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`.
- parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory.
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration.
- parameter sourceBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`.
- parameter sourceBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`.
*/
public func upgradeSQLiteStoreIfNeeded(fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, sourceBundles: [NSBundle]? = nil, completion: (MigrationResult) -> Void) -> MigrationType? {
public func upgradeSQLiteStoreIfNeeded(fileURL fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, sourceBundles: [NSBundle]? = nil, completion: (MigrationResult) -> Void) -> MigrationType? {
var metadataError: NSError?
let metadata: [NSObject: AnyObject]! = NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(
NSSQLiteStoreType,
URL: fileURL,
error: &metadataError
)
if metadata == nil {
let metadata: [String: AnyObject]
do {
let error = metadataError ?? NSError(coreStoreErrorCode: .UnknownError)
metadata = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(
NSSQLiteStoreType,
URL: fileURL
)
}
catch {
let metadataError = error as NSError
CoreStore.handleError(
error,
"Failed to load SQLite <\(NSPersistentStore.self)> metadata at \"\(fileURL)\"."
metadataError,
"Failed to load SQLite \(typeName(NSPersistentStore)) metadata at \"\(fileURL)\"."
)
GCDQueue.Main.async {
completion(MigrationResult(error))
completion(MigrationResult(metadataError))
}
return nil
}
let coordinator = self.coordinator;
let destinationModel = coordinator.managedObjectModel
if destinationModel.isConfiguration(
configuration,
compatibleWithStoreMetadata: metadata) {
GCDQueue.Main.async {
completion(MigrationResult(.None))
}
return .None
}
let sourceModel = NSManagedObjectModel(
byMergingModels: [destinationModel],
forStoreMetadata: metadata
)!
if let mappingModel = NSMappingModel(
fromBundles: sourceBundles,
forSourceModel: sourceModel,
destinationModel: destinationModel) {
self.startMigrationForSQLiteStore(
fileURL,
sourceModel: sourceModel,
destinationModel: destinationModel,
mappingModel: mappingModel,
migrationType: .Heavyweight,
completion: completion
)
return .Heavyweight
}
if let mappingModel = NSMappingModel.inferredMappingModelForSourceModel(
sourceModel,
destinationModel: destinationModel,
error: nil) {
self.startMigrationForSQLiteStore(
fileURL,
sourceModel: sourceModel,
destinationModel: destinationModel,
mappingModel: mappingModel,
migrationType: .Lightweight,
completion: completion
)
return .Lightweight
}
CoreStore.handleError(
NSError(coreStoreErrorCode: .UnknownError),
"Failed to load an <\(NSMappingModel.self)> for migration from version model \"\(sourceModel)\" to version model \"\(destinationModel)\"."
)
GCDQueue.Main.async {
guard let migrationSteps = self.computeMigrationFromStoreMetadata(metadata, configuration: configuration, sourceBundles: sourceBundles) else {
completion(MigrationResult(.MappingModelNotFound))
CoreStore.handleError(
NSError(coreStoreErrorCode: .MappingModelNotFound),
"Failed to find migration steps from the store at URL \"\(fileURL)\" to version model \"\(model)\"."
)
GCDQueue.Main.async {
completion(MigrationResult(.MappingModelNotFound))
}
return nil
}
return nil
if migrationSteps.count == 0 {
GCDQueue.Main.async {
completion(MigrationResult(.None))
}
return .None
}
var mergedMigrationType = MigrationType.None
var migrationResult: MigrationResult?
var operations = [NSOperation]()
var cancelled = false
for (sourceModel, destinationModel, mappingModel, migrationType) in migrationSteps {
switch (mergedMigrationType, migrationType) {
case (.None, _), (.Lightweight, .Heavyweight):
mergedMigrationType = migrationType
default:
break
}
operations.append(
NSBlockOperation { [weak self] in
guard let strongSelf = self where !cancelled else {
return
}
autoreleasepool {
do {
try strongSelf.startMigrationForSQLiteStore(
fileURL: fileURL,
sourceModel: sourceModel,
destinationModel: destinationModel,
mappingModel: mappingModel
)
}
catch {
migrationResult = MigrationResult(error as NSError)
cancelled = true
}
}
}
)
}
let migrationOperation = NSBlockOperation()
migrationOperation.qualityOfService = .Utility
operations.map { migrationOperation.addDependency($0) }
migrationOperation.addExecutionBlock { () -> Void in
GCDQueue.Main.async {
completion(migrationResult ?? MigrationResult(mergedMigrationType))
return
}
}
operations.append(migrationOperation)
self.migrationQueue.addOperations(operations, waitUntilFinished: false)
return mergedMigrationType
}
/**
Asynchronously adds to the stack an SQLite store from the given SQLite file name. Note that using `addSQLiteStore(...)` instead of `addSQLiteStoreAndWait(...)` implies that the migrations are allowed and expected (thus the asynchronous `completion`.)
:param: fileName the local filename for the SQLite persistent store in the "Application Support" directory. A new SQLite file will be created if it does not exist. Note that if you have multiple configurations, you will need to specify a different `fileName` explicitly for each of them.
:param: configuration an optional configuration name from the model file. If not specified, defaults to `nil`, the "Default" configuration. Note that if you have multiple configurations, you will need to specify a different `fileName` explicitly for each of them.
:param: completion the closure to be executed on the main queue when the process completes, either due to success or failure. The closure's `PersistentStoreResult` argument indicates the result.
- parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory. A new SQLite file will be created if it does not exist. Note that if you have multiple configurations, you will need to specify a different `fileName` explicitly for each of them.
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil`, the "Default" configuration. Note that if you have multiple configurations, you will need to specify a different `fileName` explicitly for each of them.
- parameter completion: the closure to be executed on the main queue when the process completes, either due to success or failure. The closure's `PersistentStoreResult` argument indicates the result.
*/
public func addSQLiteStore(fileName: String, configuration: String? = nil, sourceBundles: [NSBundle]? = nil, completion: (PersistentStoreResult) -> Void) {
@@ -247,39 +278,16 @@ public extension DataStack {
/**
Asynchronously adds to the stack an SQLite store from the given SQLite file URL. Note that using `addSQLiteStore(...)` instead of `addSQLiteStoreAndWait(...)` implies that the migrations are allowed and expected (thus the asynchronous `completion`.)
:param: fileURL the local file URL for the SQLite persistent store. A new SQLite file will be created if it does not exist. If not specified, defaults to a file URL pointing to a "<Application name>.sqlite" file in the "Application Support" directory. Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them.
:param: configuration an optional configuration name from the model file. If not specified, defaults to `nil`, the "Default" configuration. Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them.
:param: completion the closure to be executed on the main queue when the process completes, either due to success or failure. The closure's `PersistentStoreResult` argument indicates the result.
- parameter fileURL: the local file URL for the SQLite persistent store. A new SQLite file will be created if it does not exist. If not specified, defaults to a file URL pointing to a "<Application name>.sqlite" file in the "Application Support" directory. Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them.
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil`, the "Default" configuration. Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them.
- parameter completion: the closure to be executed on the main queue when the process completes, either due to success or failure. The closure's `PersistentStoreResult` argument indicates the result.
*/
public func addSQLiteStore(fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, sourceBundles: [NSBundle]? = nil, completion: (PersistentStoreResult) -> Void) {
if NSFileManager.defaultManager().fileExistsAtPath(fileURL.path!) {
var error: NSError?
let metadata = NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(
NSSQLiteStoreType,
URL: fileURL,
error: &error
)
if metadata == nil {
CoreStore.handleError(
error ?? NSError(coreStoreErrorCode: .UnknownError),
"Failed to load SQLite <\(NSPersistentStore.self)> metadata at \"\(fileURL)\"."
)
GCDQueue.Main.async {
completion(PersistentStoreResult(.UnknownError))
}
return
}
}
public func addSQLiteStore(fileURL fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, sourceBundles: [NSBundle]? = NSBundle.allBundles(), completion: (PersistentStoreResult) -> Void) {
let coordinator = self.coordinator;
if let store = coordinator.persistentStoreForURL(fileURL) {
let isExistingStoreAutomigrating = ((store.options?[NSMigratePersistentStoresAutomaticallyOption] as? Bool) ?? false)
let isExistingStoreAutomigrating = store.options?[NSMigratePersistentStoresAutomaticallyOption] as? Bool == true
if store.type == NSSQLiteStoreType
&& isExistingStoreAutomigrating
@@ -294,7 +302,7 @@ public extension DataStack {
CoreStore.handleError(
NSError(coreStoreErrorCode: .DifferentPersistentStoreExistsAtURL),
"Failed to add SQLite <\(NSPersistentStore.self)> at \"\(fileURL)\" because a different <\(NSPersistentStore.self)> at that URL already exists."
"Failed to add SQLite \(typeName(NSPersistentStore)) at \"\(fileURL)\" because a different \(typeName(NSPersistentStore)) at that URL already exists."
)
GCDQueue.Main.async {
@@ -304,151 +312,239 @@ public extension DataStack {
return
}
let fileManager = NSFileManager.defaultManager()
var directoryError: NSError?
if !fileManager.createDirectoryAtURL(
fileURL.URLByDeletingLastPathComponent!,
withIntermediateDirectories: true,
attributes: nil,
error: &directoryError) {
do {
try NSFileManager.defaultManager().createDirectoryAtURL(
fileURL.URLByDeletingLastPathComponent!,
withIntermediateDirectories: true,
attributes: nil
)
}
catch _ { }
self.upgradeSQLiteStoreIfNeeded(
fileURL: fileURL,
configuration: configuration,
sourceBundles: sourceBundles,
completion: { (result) -> Void in
CoreStore.handleError(
directoryError ?? NSError(coreStoreErrorCode: .UnknownError),
"Failed to create directory for SQLite store at \"\(fileURL)\"."
if case .Failure(let error) = result
where error.domain != NSCocoaErrorDomain || error.code != NSFileReadNoSuchFileError {
completion(PersistentStoreResult(error))
return
}
let persistentStoreResult = self.addSQLiteStoreAndWait(
fileURL: fileURL,
configuration: configuration,
automigrating: false,
resetStoreOnMigrationFailure: false
)
GCDQueue.Main.async {
completion(PersistentStoreResult(directoryError!))
}
return
}
coordinator.performBlock {
var persistentStoreError: NSError?
let store = coordinator.addPersistentStoreWithType(
NSSQLiteStoreType,
configuration: configuration,
URL: fileURL,
options: [NSSQLitePragmasOption: ["WAL": "journal_mode"],
NSInferMappingModelAutomaticallyOption: true,
NSMigratePersistentStoresAutomaticallyOption: true],
error: &persistentStoreError)
if let store = store {
GCDQueue.Main.async {
self.updateMetadataForPersistentStore(store)
completion(PersistentStoreResult(store))
}
completion(persistentStoreResult)
}
else {
GCDQueue.Main.async {
CoreStore.handleError(
persistentStoreError ?? NSError(coreStoreErrorCode: .UnknownError),
"Failed to add SQLite <\(NSPersistentStore.self)> at \"\(fileURL)\"."
)
completion(PersistentStoreResult(.UnknownError))
}
}
}
)
}
// MARK: Private
private func startMigrationForSQLiteStore(fileURL: NSURL, sourceModel: NSManagedObjectModel, destinationModel: NSManagedObjectModel, mappingModel: NSMappingModel, migrationType: MigrationType, completion: (MigrationResult) -> Void) {
private func computeMigrationFromStoreMetadata(metadata: [String: AnyObject], configuration: String? = nil, sourceBundles: [NSBundle]? = nil) -> [(sourceModel: NSManagedObjectModel, destinationModel: NSManagedObjectModel, mappingModel: NSMappingModel, migrationType: MigrationType)]? {
let model = self.model
if model.isConfiguration(configuration, compatibleWithStoreMetadata: metadata) {
return []
}
let metadataModel = NSManagedObjectModel(byMergingModels: model.mergedModels(), forStoreMetadata: metadata)!
if let bypassModel = NSMappingModel(
fromBundles: sourceBundles,
forSourceModel: metadataModel,
destinationModel: model) {
return [
(
sourceModel: metadataModel,
destinationModel: model,
mappingModel: bypassModel,
migrationType: .Heavyweight
)
]
}
var initialModel: NSManagedObjectModel?
if let modelHashes = metadata[NSStoreModelVersionHashesKey] as? [String : NSData],
let modelVersions = model.modelVersions {
for modelVersion in modelVersions {
if let versionModel = model[modelVersion] where modelHashes == versionModel.entityVersionHashesByName {
initialModel = versionModel
break
}
}
}
guard var currentVersion = initialModel?.currentModelVersion else {
return nil
}
let migrationChain = self.migrationChain
var migrationSteps = [(sourceModel: NSManagedObjectModel, destinationModel: NSManagedObjectModel, mappingModel: NSMappingModel, migrationType: MigrationType)]()
while let nextVersion = migrationChain.nextVersionFrom(currentVersion),
let sourceModel = model[currentVersion],
let destinationModel = model[nextVersion] {
if let mappingModel = NSMappingModel(
fromBundles: sourceBundles,
forSourceModel: sourceModel,
destinationModel: destinationModel) {
migrationSteps.append(
sourceModel: sourceModel,
destinationModel: destinationModel,
mappingModel: mappingModel,
migrationType: .Heavyweight
)
}
else {
do {
let mappingModel = try NSMappingModel.inferredMappingModelForSourceModel(
sourceModel,
destinationModel: destinationModel
)
migrationSteps.append(
sourceModel: sourceModel,
destinationModel: destinationModel,
mappingModel: mappingModel,
migrationType: .Lightweight
)
}
catch {
return nil
}
}
currentVersion = nextVersion
}
if migrationSteps.last?.destinationModel == model {
return migrationSteps
}
return nil
}
private func startMigrationForSQLiteStore(fileURL fileURL: NSURL, sourceModel: NSManagedObjectModel, destinationModel: NSManagedObjectModel, mappingModel: NSMappingModel) throws {
let migrationManager = NSMigrationManager(
sourceModel: sourceModel,
destinationModel: destinationModel
)
self.migrationQueue.async {
var lastReportedProgress: Float = -1
let timer = GCDTimer.createSuspended(
.Main,
interval: 0.1,
eventHandler: { (timer) -> Void in
var lastReportedProgress: Float = -1
let timer = GCDTimer.createSuspended(
.Main,
interval: 0.1,
eventHandler: { (timer) -> Void in
let progress = migrationManager.migrationProgress
if progress > lastReportedProgress {
let progress = migrationManager.migrationProgress
if progress > lastReportedProgress {
// TODO: progress
CoreStore.log(.Trace, message: "migration progress: \(progress)")
lastReportedProgress = progress
}
// TODO: progress
CoreStore.log(.Trace, message: "migration progress: \(progress)")
lastReportedProgress = progress
}
)
}
)
let temporaryDirectoryURL = NSURL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).URLByAppendingPathComponent(NSProcessInfo().globallyUniqueString)
let fileManager = NSFileManager.defaultManager()
try! fileManager.createDirectoryAtURL(
temporaryDirectoryURL,
withIntermediateDirectories: true,
attributes: nil
)
do {
let temporaryFileURL = NSURL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)!.URLByAppendingPathComponent(NSProcessInfo().globallyUniqueString)
var migrationError: NSError?
let migrationCompleted = migrationManager.migrateStoreFromURL(
try migrationManager.migrateStoreFromURL(
fileURL,
type: NSSQLiteStoreType,
options: nil,
options: [
NSSQLitePragmasOption: ["WAL": "journal_mode"]
],
withMappingModel: mappingModel,
toDestinationURL: temporaryFileURL,
toDestinationURL: temporaryDirectoryURL.URLByAppendingPathComponent(fileURL.lastPathComponent!, isDirectory: false),
destinationType: NSSQLiteStoreType,
destinationOptions: nil,
error: &migrationError
destinationOptions: [
NSSQLitePragmasOption: ["WAL": "journal_mode"],
NSSQLiteManualVacuumOption: true
]
)
}
catch {
timer.suspend()
let fileManager = NSFileManager.defaultManager()
if !migrationCompleted {
do {
fileManager.removeItemAtURL(temporaryFileURL, error: nil)
try fileManager.removeItemAtURL(temporaryDirectoryURL)
}
catch _ { }
let migrationError = error as NSError
CoreStore.handleError(
migrationError,
"Failed to migrate from version model \"\(migrationManager.sourceModel)\" to version model \"\(migrationManager.destinationModel)\"."
)
throw error
}
timer.suspend()
do {
let originalDirectoryURL = fileURL.URLByDeletingLastPathComponent!
for temporaryFileURL in try fileManager.contentsOfDirectoryAtURL(temporaryDirectoryURL, includingPropertiesForKeys: nil, options: .SkipsSubdirectoryDescendants) {
let error = migrationError ?? NSError(coreStoreErrorCode: .UnknownError)
CoreStore.handleError(
error,
"Failed to migrate from version model \"\(migrationManager.sourceModel)\" to version model \"\(migrationManager.destinationModel)\"."
try fileManager.replaceItemAtURL(
originalDirectoryURL.URLByAppendingPathComponent(
temporaryFileURL.lastPathComponent!,
isDirectory: false
),
withItemAtURL: temporaryFileURL,
backupItemName: nil,
options: [],
resultingItemURL: nil
)
GCDQueue.Main.async {
completion(MigrationResult(error))
}
return
}
}
catch {
var replaceError: NSError?
if !fileManager.replaceItemAtURL(
fileURL,
withItemAtURL: temporaryFileURL,
backupItemName: nil,
options: .allZeros,
resultingItemURL: nil,
error: &replaceError) {
fileManager.removeItemAtURL(temporaryFileURL, error: nil)
let error = replaceError ?? NSError(coreStoreErrorCode: .UnknownError)
CoreStore.handleError(
error,
"Failed to save store after migrating from version model \"\(migrationManager.sourceModel)\" to version model \"\(migrationManager.destinationModel)\"."
)
GCDQueue.Main.async {
completion(MigrationResult(error))
}
return
}
GCDQueue.Main.async {
do {
completion(MigrationResult(migrationType))
try fileManager.removeItemAtURL(temporaryDirectoryURL)
}
catch _ { }
let replaceError = error as NSError
CoreStore.handleError(
replaceError,
"Failed to save store after migrating from version model \"\(migrationManager.sourceModel)\" to version model \"\(migrationManager.destinationModel)\"."
)
throw error
}
}
}

View File

@@ -37,12 +37,15 @@ public extension DataStack {
/**
Creates a `ObjectMonitor` for the specified `NSManagedObject`. Multiple `ObjectObserver`s may then register themselves to be notified when changes are made to the `NSManagedObject`.
:param: object the `NSManagedObject` to observe changes from
:returns: a `ObjectMonitor` that monitors changes to `object`
- parameter object: the `NSManagedObject` to observe changes from
- returns: a `ObjectMonitor` that monitors changes to `object`
*/
public func monitorObject<T: NSManagedObject>(object: T) -> ObjectMonitor<T> {
CoreStore.assert(NSThread.isMainThread(), "Attempted to observe objects from \(typeName(self)) outside the main thread.")
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to observe objects from \(typeName(self)) outside the main thread."
)
return ObjectMonitor(
dataStack: self,
@@ -53,9 +56,9 @@ public extension DataStack {
/**
Creates a `ListMonitor` for a list of `NSManagedObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: a `ListMonitor` instance that monitors changes to the list
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: a `ListMonitor` instance that monitors changes to the list
*/
public func monitorList<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> ListMonitor<T> {
@@ -65,13 +68,16 @@ public extension DataStack {
/**
Creates a `ListMonitor` for a list of `NSManagedObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: a `ListMonitor` instance that monitors changes to the list
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: a `ListMonitor` instance that monitors changes to the list
*/
public func monitorList<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> ListMonitor<T> {
CoreStore.assert(NSThread.isMainThread(), "Attempted to observe objects from \(typeName(self)) outside the main thread.")
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to observe objects from \(typeName(self)) outside the main thread."
)
return ListMonitor(
dataStack: self,
@@ -84,10 +90,10 @@ public extension DataStack {
/**
Creates a `ListMonitor` for a sectioned list of `NSManagedObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
:param: from a `From` clause indicating the entity type
:param: sectionBy a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
:param: fetchClauses a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: a `ListMonitor` instance that monitors changes to the list
- parameter from: a `From` clause indicating the entity type
- parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: a `ListMonitor` instance that monitors changes to the list
*/
public func monitorSectionedList<T: NSManagedObject>(from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> ListMonitor<T> {
@@ -97,14 +103,17 @@ public extension DataStack {
/**
Creates a `ListMonitor` for a sectioned list of `NSManagedObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
:param: from a `From` clause indicating the entity type
:param: sectionBy a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
:param: fetchClauses a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: a `ListMonitor` instance that monitors changes to the list
- parameter from: a `From` clause indicating the entity type
- parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: a `ListMonitor` instance that monitors changes to the list
*/
public func monitorSectionedList<T: NSManagedObject>(from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> ListMonitor<T> {
CoreStore.assert(NSThread.isMainThread(), "Attempted to observe objects from \(typeName(self)) outside the main thread.")
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to observe objects from \(typeName(self)) outside the main thread."
)
return ListMonitor(
dataStack: self,

View File

@@ -37,11 +37,14 @@ public extension DataStack {
/**
Begins a transaction asynchronously where `NSManagedObject` creates, updates, and deletes can be made.
:param: closure the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
- parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
*/
public func beginAsynchronous(closure: (transaction: AsynchronousDataTransaction) -> Void) {
CoreStore.assert(NSThread.isMainThread(), "Attempted to begin a transaction from a \(typeName(self)) outside the main thread.")
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to begin a transaction from a \(typeName(self)) outside the main thread."
)
AsynchronousDataTransaction(
mainContext: self.rootSavingContext,
@@ -52,12 +55,15 @@ public extension DataStack {
/**
Begins a transaction synchronously where `NSManagedObject` creates, updates, and deletes can be made.
:param: closure the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
:returns: a `SaveResult` value indicating success or failure, or `nil` if the transaction was not comitted synchronously
- parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
- returns: a `SaveResult` value indicating success or failure, or `nil` if the transaction was not comitted synchronously
*/
public func beginSynchronous(closure: (transaction: SynchronousDataTransaction) -> Void) -> SaveResult? {
CoreStore.assert(NSThread.isMainThread(), "Attempted to begin a transaction from a \(typeName(self)) outside the main thread.")
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to begin a transaction from a \(typeName(self)) outside the main thread."
)
return SynchronousDataTransaction(
mainContext: self.rootSavingContext,
@@ -68,11 +74,14 @@ public extension DataStack {
/**
Begins a non-contiguous transaction where `NSManagedObject` creates, updates, and deletes can be made. This is useful for making temporary changes, such as partially filled forms. A detached transaction object should typically be only used from the main queue.
:returns: a `DetachedDataTransaction` instance where creates, updates, and deletes can be made.
- returns: a `DetachedDataTransaction` instance where creates, updates, and deletes can be made.
*/
public func beginDetached() -> DetachedDataTransaction {
CoreStore.assert(NSThread.isMainThread(), "Attempted to begin a transaction from a \(typeName(self)) outside the main thread.")
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to begin a transaction from a \(typeName(self)) outside the main thread."
)
return DetachedDataTransaction(
mainContext: self.rootSavingContext,

View File

@@ -28,9 +28,9 @@ import CoreData
import GCDKit
internal let applicationSupportDirectory = NSFileManager.defaultManager().URLsForDirectory(.ApplicationSupportDirectory, inDomains: .UserDomainMask).first as! NSURL
internal let applicationSupportDirectory = NSFileManager.defaultManager().URLsForDirectory(.ApplicationSupportDirectory, inDomains: .UserDomainMask).first!
internal let applicationName = ((NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleName") as? String) ?? "CoreData")
internal let applicationName = (NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleName") as? String) ?? "CoreData"
internal let defaultSQLiteStoreURL = applicationSupportDirectory.URLByAppendingPathComponent(applicationName, isDirectory: false).URLByAppendingPathExtension("sqlite")
@@ -47,47 +47,26 @@ public final class DataStack {
/**
Initializes a `DataStack` from an `NSManagedObjectModel`.
:param: modelName the name of the (.xcdatamodeld) model file. If not specified, the application name will be used
:param: sourceBundle an optional bundle to load models from. If not specified, the main bundle will be used.
:param: modelVersions the `MigrationChain` that indicates the heirarchy of the model's version names. If not specified, will default to a non-migrating data stack.
- parameter modelName: the name of the (.xcdatamodeld) model file. If not specified, the application name will be used
- parameter bundle: an optional bundle to load models from. If not specified, the main bundle will be used.
- parameter migrationChain: the `MigrationChain` that indicates the heirarchy of the model's version names. If not specified, will default to a non-migrating data stack.
*/
public required init(modelName: String = applicationName, sourceBundle: NSBundle = NSBundle.mainBundle(), modelVersions: MigrationChain = nil) {
public required init(modelName: String = applicationName, bundle: NSBundle = NSBundle.mainBundle(), migrationChain: MigrationChain = nil) {
let modelFilePath: String! = sourceBundle.pathForResource(
modelName,
ofType: "momd"
)
CoreStore.assert(
modelFilePath != nil,
"Could not find a \"momd\" resource from the main bundle."
let model = NSManagedObjectModel.fromBundle(
bundle,
modelName: modelName,
modelVersion: migrationChain.leafVersions.first
)
let managedObjectModel: NSManagedObjectModel! = NSManagedObjectModel(contentsOfURL: NSURL(fileURLWithPath: modelFilePath)!)
CoreStore.assert(
managedObjectModel != nil,
"Could not create an <\(NSManagedObjectModel.self)> from the resource at path \"\(modelFilePath)\"."
)
// TODO: assert existence of all model versions in the migrationChain
self.coordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
self.coordinator = NSPersistentStoreCoordinator(managedObjectModel: model)
self.rootSavingContext = NSManagedObjectContext.rootSavingContextForCoordinator(self.coordinator)
self.mainContext = NSManagedObjectContext.mainContextForRootContext(self.rootSavingContext)
self.sourceBundle = sourceBundle
self.modelVersions = modelVersions
var entityNameMapping = [EntityClassNameType: EntityNameType]()
var entityConfigurationsMapping = [EntityClassNameType: Set<String>]()
for entityDescription in managedObjectModel.entities as! [NSEntityDescription] {
let managedObjectClassName = entityDescription.managedObjectClassName
entityConfigurationsMapping[managedObjectClassName] = []
if let entityName = entityDescription.name {
entityNameMapping[managedObjectClassName] = entityName
}
}
self.entityNameMapping = entityNameMapping
self.entityConfigurationsMapping = entityConfigurationsMapping
self.bundle = bundle
self.model = model
self.migrationChain = migrationChain
self.rootSavingContext.parentStack = self
}
@@ -95,23 +74,30 @@ public final class DataStack {
/**
Adds an in-memory store to the stack.
:param: configuration an optional configuration name from the model file. If not specified, defaults to nil.
:returns: a `PersistentStoreResult` indicating success or failure.
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to nil.
- returns: a `PersistentStoreResult` indicating success or failure.
*/
public func addInMemoryStore(configuration: String? = nil) -> PersistentStoreResult {
public func addInMemoryStore(configuration configuration: String? = nil) -> PersistentStoreResult {
let coordinator = self.coordinator;
var error: NSError?
var store: NSPersistentStore?
var storeError: NSError?
coordinator.performBlockAndWait {
store = coordinator.addPersistentStoreWithType(
NSInMemoryStoreType,
configuration: configuration,
URL: nil,
options: nil,
error: &error)
do {
store = try coordinator.addPersistentStoreWithType(
NSInMemoryStoreType,
configuration: configuration,
URL: nil,
options: nil
)
}
catch {
storeError = error as NSError
}
}
if let store = store {
@@ -120,18 +106,20 @@ public final class DataStack {
return PersistentStoreResult(store)
}
if let error = error {
if let error = storeError {
CoreStore.handleError(
error,
"Failed to add in-memory <\(NSPersistentStore.self)>.")
"Failed to add in-memory \(typeName(NSPersistentStore))."
)
return PersistentStoreResult(error)
}
else {
CoreStore.handleError(
NSError(coreStoreErrorCode: .UnknownError),
"Failed to add in-memory <\(NSPersistentStore.self)>.")
"Failed to add in-memory \(typeName(NSPersistentStore))."
)
return PersistentStoreResult(.UnknownError)
}
}
@@ -139,11 +127,11 @@ public final class DataStack {
/**
Adds to the stack an SQLite store from the given SQLite file name.
:param: fileName the local filename for the SQLite persistent store in the "Application Support" directory. A new SQLite file will be created if it does not exist. Note that if you have multiple configurations, you will need to specify a different `fileName` explicitly for each of them.
:param: configuration an optional configuration name from the model file. If not specified, defaults to `nil`, the "Default" configuration. Note that if you have multiple configurations, you will need to specify a different `fileName` explicitly for each of them.
:param: automigrating Set to true to configure Core Data auto-migration, or false to disable. If not specified, defaults to true.
:param: resetStoreOnMigrationFailure Set to true to delete the store on migration failure; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false
:returns: a `PersistentStoreResult` indicating success or failure.
- parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory. A new SQLite file will be created if it does not exist. Note that if you have multiple configurations, you will need to specify a different `fileName` explicitly for each of them.
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil`, the "Default" configuration. Note that if you have multiple configurations, you will need to specify a different `fileName` explicitly for each of them.
- parameter automigrating: Set to true to configure Core Data auto-migration, or false to disable. If not specified, defaults to true.
- parameter resetStoreOnMigrationFailure: Set to true to delete the store on migration failure; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false
- returns: a `PersistentStoreResult` indicating success or failure.
*/
public func addSQLiteStoreAndWait(fileName: String, configuration: String? = nil, automigrating: Bool = true, resetStoreOnMigrationFailure: Bool = false) -> PersistentStoreResult {
@@ -161,18 +149,18 @@ public final class DataStack {
/**
Adds to the stack an SQLite store from the given SQLite file URL.
:param: fileURL the local file URL for the SQLite persistent store. A new SQLite file will be created if it does not exist. If not specified, defaults to a file URL pointing to a "<Application name>.sqlite" file in the "Application Support" directory. Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them.
:param: configuration an optional configuration name from the model file. If not specified, defaults to `nil`, the "Default" configuration. Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them.
:param: automigrating Set to true to configure Core Data auto-migration, or false to disable. If not specified, defaults to true.
:param: resetStoreOnMigrationFailure Set to true to delete the store on migration failure; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false.
:returns: a `PersistentStoreResult` indicating success or failure.
- parameter fileURL: the local file URL for the SQLite persistent store. A new SQLite file will be created if it does not exist. If not specified, defaults to a file URL pointing to a "<Application name>.sqlite" file in the "Application Support" directory. Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them.
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil`, the "Default" configuration. Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them.
- parameter automigrating: Set to true to configure Core Data auto-migration, or false to disable. If not specified, defaults to true.
- parameter resetStoreOnMigrationFailure: Set to true to delete the store on migration failure; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false.
- returns: a `PersistentStoreResult` indicating success or failure.
*/
public func addSQLiteStoreAndWait(fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, automigrating: Bool = true, resetStoreOnMigrationFailure: Bool = false) -> PersistentStoreResult {
public func addSQLiteStoreAndWait(fileURL fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, automigrating: Bool = true, resetStoreOnMigrationFailure: Bool = false) -> PersistentStoreResult {
let coordinator = self.coordinator;
if let store = coordinator.persistentStoreForURL(fileURL) {
let isExistingStoreAutomigrating = ((store.options?[NSMigratePersistentStoresAutomaticallyOption] as? Bool) ?? false)
let isExistingStoreAutomigrating = (store.options?[NSMigratePersistentStoresAutomaticallyOption] as? Bool) == true
if store.type == NSSQLiteStoreType
&& isExistingStoreAutomigrating == automigrating
@@ -183,37 +171,44 @@ public final class DataStack {
CoreStore.handleError(
NSError(coreStoreErrorCode: .DifferentPersistentStoreExistsAtURL),
"Failed to add SQLite <\(NSPersistentStore.self)> at \"\(fileURL)\" because a different <\(NSPersistentStore.self)> at that URL already exists.")
"Failed to add SQLite \(typeName(NSPersistentStore)) at \"\(fileURL)\" because a different \(typeName(NSPersistentStore)) at that URL already exists."
)
return PersistentStoreResult(.DifferentPersistentStoreExistsAtURL)
}
let fileManager = NSFileManager.defaultManager()
var directoryError: NSError?
if !fileManager.createDirectoryAtURL(
fileURL.URLByDeletingLastPathComponent!,
withIntermediateDirectories: true,
attributes: nil,
error: &directoryError) {
CoreStore.handleError(
directoryError ?? NSError(coreStoreErrorCode: .UnknownError),
"Failed to create directory for SQLite store at \"\(fileURL)\".")
return PersistentStoreResult(directoryError!)
do {
try fileManager.createDirectoryAtURL(
fileURL.URLByDeletingLastPathComponent!,
withIntermediateDirectories: true,
attributes: nil
)
}
catch _ { }
var store: NSPersistentStore?
var persistentStoreError: NSError?
var storeError: NSError?
coordinator.performBlockAndWait {
store = coordinator.addPersistentStoreWithType(
NSSQLiteStoreType,
configuration: configuration,
URL: fileURL,
options: [NSSQLitePragmasOption: ["WAL": "journal_mode"],
NSInferMappingModelAutomaticallyOption: true,
NSMigratePersistentStoresAutomaticallyOption: automigrating],
error: &persistentStoreError)
do {
store = try coordinator.addPersistentStoreWithType(
NSSQLiteStoreType,
configuration: configuration,
URL: fileURL,
options: [
NSSQLitePragmasOption: ["WAL": "journal_mode"],
NSInferMappingModelAutomaticallyOption: true,
NSMigratePersistentStoresAutomaticallyOption: automigrating
]
)
}
catch {
storeError = error as NSError
}
}
if let store = store {
@@ -222,7 +217,7 @@ public final class DataStack {
return PersistentStoreResult(store)
}
if let error = persistentStoreError
if let error = storeError
where (
resetStoreOnMigrationFailure
&& (error.code == NSPersistentStoreIncompatibleVersionHashError
@@ -231,25 +226,44 @@ public final class DataStack {
&& error.domain == NSCocoaErrorDomain
) {
fileManager.removeItemAtURL(fileURL, error: nil)
fileManager.removeItemAtPath(
fileURL.path!.stringByAppendingString("-shm"),
error: nil)
fileManager.removeItemAtPath(
fileURL.path!.stringByAppendingString("-wal"),
error: nil)
do {
try fileManager.removeItemAtURL(fileURL)
}
catch _ { }
do {
try fileManager.removeItemAtPath(fileURL.path!.stringByAppendingString("-shm"))
}
catch _ { }
do {
try fileManager.removeItemAtPath(fileURL.path!.stringByAppendingString("-wal"))
}
catch _ { }
var store: NSPersistentStore?
coordinator.performBlockAndWait {
store = coordinator.addPersistentStoreWithType(
NSSQLiteStoreType,
configuration: configuration,
URL: fileURL,
options: [NSSQLitePragmasOption: ["WAL": "journal_mode"],
NSInferMappingModelAutomaticallyOption: true,
NSMigratePersistentStoresAutomaticallyOption: automigrating],
error: &persistentStoreError)
do {
store = try coordinator.addPersistentStoreWithType(
NSSQLiteStoreType,
configuration: configuration,
URL: fileURL,
options: [
NSSQLitePragmasOption: ["WAL": "journal_mode"],
NSInferMappingModelAutomaticallyOption: true,
NSMigratePersistentStoresAutomaticallyOption: automigrating
]
)
}
catch {
storeError = error as NSError
}
}
if let store = store {
@@ -260,8 +274,9 @@ public final class DataStack {
}
CoreStore.handleError(
persistentStoreError ?? NSError(coreStoreErrorCode: .UnknownError),
"Failed to add SQLite <\(NSPersistentStore.self)> at \"\(fileURL)\".")
storeError ?? NSError(coreStoreErrorCode: .UnknownError),
"Failed to add SQLite \(typeName(NSPersistentStore)) at \"\(fileURL)\"."
)
return PersistentStoreResult(.UnknownError)
}
@@ -272,14 +287,23 @@ public final class DataStack {
internal let coordinator: NSPersistentStoreCoordinator
internal let rootSavingContext: NSManagedObjectContext
internal let mainContext: NSManagedObjectContext
internal let sourceBundle: NSBundle
internal let modelVersions: MigrationChain
internal let bundle: NSBundle
internal let model: NSManagedObjectModel
internal let migrationChain: MigrationChain
internal let childTransactionQueue: GCDQueue = .createSerial("com.corestore.datastack.childtransactionqueue")
internal let migrationQueue: GCDQueue = .createSerial("com.corestore.datastack.migrationqueue")
internal let migrationQueue: NSOperationQueue = {
let migrationQueue = NSOperationQueue()
migrationQueue.maxConcurrentOperationCount = 1
migrationQueue.name = "com.coreStore.migrationOperationQueue"
migrationQueue.qualityOfService = .Utility
migrationQueue.underlyingQueue = dispatch_queue_create("com.coreStore.migrationQueue", DISPATCH_QUEUE_SERIAL)
return migrationQueue
}()
internal func entityNameForEntityClass(entityClass: AnyClass) -> String? {
return self.entityNameMapping[NSStringFromClass(entityClass)]
return self.model.entityNameForClass(entityClass)
}
internal func persistentStoresForEntityClass(entityClass: AnyClass) -> [NSPersistentStore]? {
@@ -287,11 +311,10 @@ public final class DataStack {
var returnValue: [NSPersistentStore]? = nil
self.storeMetadataUpdateQueue.barrierSync {
let configurationsForEntity = self.entityConfigurationsMapping[NSStringFromClass(entityClass)] ?? []
returnValue = map(configurationsForEntity) {
returnValue = self.entityConfigurationsMapping[NSStringFromClass(entityClass)]?.map {
return self.configurationStoreMapping[$0]!
}
} ?? []
}
return returnValue
}
@@ -336,8 +359,12 @@ public final class DataStack {
let configurationName = persistentStore.configurationName
self.configurationStoreMapping[configurationName] = persistentStore
for entityDescription in (self.coordinator.managedObjectModel.entitiesForConfiguration(configurationName) as? [NSEntityDescription] ?? []) {
for entityDescription in (self.coordinator.managedObjectModel.entitiesForConfiguration(configurationName) ?? []) {
if self.entityConfigurationsMapping[entityDescription.managedObjectClassName] == nil {
self.entityConfigurationsMapping[entityDescription.managedObjectClassName] = []
}
self.entityConfigurationsMapping[entityDescription.managedObjectClassName]?.insert(configurationName)
}
}
@@ -346,12 +373,7 @@ public final class DataStack {
// MARK: Private
private typealias EntityClassNameType = String
private typealias EntityNameType = String
private typealias ConfigurationNameType = String
private let entityNameMapping: [EntityClassNameType: EntityNameType]
private let storeMetadataUpdateQueue = GCDQueue.createConcurrent("com.coreStore.persistentStoreBarrierQueue")
private var configurationStoreMapping = [ConfigurationNameType: NSPersistentStore]()
private var entityConfigurationsMapping = [EntityClassNameType: Set<String>]()
private var configurationStoreMapping = [String: NSPersistentStore]()
private var entityConfigurationsMapping = [String: Set<String>]()
}