updated error handling for DataStack methods, updated migration demo app

This commit is contained in:
John Rommel Estropia
2015-07-12 19:20:38 +09:00
parent 8cfe8e2500
commit a64bcc474e
28 changed files with 773 additions and 393 deletions

View File

@@ -64,6 +64,7 @@
B5E84F411AFF8CCD0064E85B /* ClauseTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F401AFF8CCD0064E85B /* ClauseTypes.swift */; }; B5E84F411AFF8CCD0064E85B /* ClauseTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F401AFF8CCD0064E85B /* ClauseTypes.swift */; };
B5FAD6A91B50A4B400714891 /* NSProgress+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FAD6A81B50A4B300714891 /* NSProgress+Convenience.swift */; }; B5FAD6A91B50A4B400714891 /* NSProgress+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FAD6A81B50A4B300714891 /* NSProgress+Convenience.swift */; };
B5FAD6AC1B51285300714891 /* MigrationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FAD6AB1B51285300714891 /* MigrationManager.swift */; }; B5FAD6AC1B51285300714891 /* MigrationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FAD6AB1B51285300714891 /* MigrationManager.swift */; };
B5FAD6AE1B518DCB00714891 /* CoreStore+Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FAD6AD1B518DCB00714891 /* CoreStore+Migration.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@@ -162,6 +163,7 @@
B5E84F401AFF8CCD0064E85B /* ClauseTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClauseTypes.swift; sourceTree = "<group>"; }; B5E84F401AFF8CCD0064E85B /* ClauseTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClauseTypes.swift; sourceTree = "<group>"; };
B5FAD6A81B50A4B300714891 /* NSProgress+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSProgress+Convenience.swift"; sourceTree = "<group>"; }; B5FAD6A81B50A4B300714891 /* NSProgress+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSProgress+Convenience.swift"; sourceTree = "<group>"; };
B5FAD6AB1B51285300714891 /* MigrationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MigrationManager.swift; sourceTree = "<group>"; }; B5FAD6AB1B51285300714891 /* MigrationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MigrationManager.swift; sourceTree = "<group>"; };
B5FAD6AD1B518DCB00714891 /* CoreStore+Migration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CoreStore+Migration.swift"; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@@ -270,6 +272,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
B56964D31B22FFAD0075EE4A /* DataStack+Migration.swift */, B56964D31B22FFAD0075EE4A /* DataStack+Migration.swift */,
B5FAD6AD1B518DCB00714891 /* CoreStore+Migration.swift */,
B56007151B4018AB00A9A8F9 /* MigrationChain.swift */, B56007151B4018AB00A9A8F9 /* MigrationChain.swift */,
B56965231B356B820075EE4A /* MigrationResult.swift */, B56965231B356B820075EE4A /* MigrationResult.swift */,
); );
@@ -553,6 +556,7 @@
B5E84F0E1AFF847B0064E85B /* Tweak.swift in Sources */, B5E84F0E1AFF847B0064E85B /* Tweak.swift in Sources */,
B5E84F121AFF847B0064E85B /* OrderBy.swift in Sources */, B5E84F121AFF847B0064E85B /* OrderBy.swift in Sources */,
B5E84F361AFF85470064E85B /* NSManagedObjectContext+Setup.swift in Sources */, B5E84F361AFF85470064E85B /* NSManagedObjectContext+Setup.swift in Sources */,
B5FAD6AE1B518DCB00714891 /* CoreStore+Migration.swift in Sources */,
B5E84EE71AFF84610064E85B /* CoreStore+Logging.swift in Sources */, B5E84EE71AFF84610064E85B /* CoreStore+Logging.swift in Sources */,
B56007111B3F6BD500A9A8F9 /* Into.swift in Sources */, B56007111B3F6BD500A9A8F9 /* Into.swift in Sources */,
B5E84F111AFF847B0064E85B /* Select.swift in Sources */, B5E84F111AFF847B0064E85B /* Select.swift in Sources */,

View File

@@ -50,9 +50,8 @@ public extension NSProgress {
get { get {
let object: AnyObject? = getAssociatedObjectForKey(&PropertyKeys.progressObserver, inObject: self) let object: ProgressObserver? = getAssociatedObjectForKey(&PropertyKeys.progressObserver, inObject: self)
if let observer = object {
if let observer = object as? ProgressObserver {
return observer return observer
} }
@@ -72,41 +71,58 @@ public extension NSProgress {
@objc private final class ProgressObserver: NSObject { @objc private final class ProgressObserver: NSObject {
private weak var progress: NSProgress? private unowned let progress: NSProgress
private var progressHandler: ((progress: NSProgress) -> Void)? private var progressHandler: ((progress: NSProgress) -> Void)? {
didSet {
let progressHandler = self.progressHandler
if (progressHandler == nil) == (oldValue == nil) {
return
}
if let _ = progressHandler {
self.progress.addObserver(
self,
forKeyPath: "fractionCompleted",
options: [.Initial, .New],
context: nil
)
}
else {
self.progress.removeObserver(self, forKeyPath: "fractionCompleted")
}
}
}
private init(_ progress: NSProgress) { private init(_ progress: NSProgress) {
self.progress = progress self.progress = progress
super.init() super.init()
progress.addObserver(
self,
forKeyPath: "fractionCompleted",
options: .New,
context: nil
)
} }
deinit { deinit {
progress?.removeObserver(self, forKeyPath: "fractionCompleted") if let _ = self.progressHandler {
self.progressHandler = nil
self.progress.removeObserver(self, forKeyPath: "fractionCompleted")
}
} }
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) { override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
guard let progress = self.progress where object as? NSProgress == progress && keyPath == "fractionCompleted" else { guard let progress = object as? NSProgress where progress == self.progress && keyPath == "fractionCompleted" else {
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
return return
} }
GCDQueue.Main.async { [weak self] () -> Void in GCDQueue.Main.async { [weak self] () -> Void in
if let strongSelf = self, let progress = strongSelf.progress { self?.progressHandler?(progress: progress)
strongSelf.progressHandler?(progress: progress)
}
} }
} }
} }

View File

@@ -52,7 +52,6 @@ internal extension NSManagedObjectModel {
let modelVersions = Set(versionHashes.keys) let modelVersions = Set(versionHashes.keys)
let currentModelVersion: String let currentModelVersion: String
if let modelVersion = modelVersion { if let modelVersion = modelVersion {
currentModelVersion = modelVersion currentModelVersion = modelVersion

View File

@@ -0,0 +1,144 @@
//
// CoreStore+Migration.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
import GCDKit
// MARK: - CoreStore
public extension CoreStore {
/**
Asynchronously adds to the `defaultStack` 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`.)
- 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 mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`.
- 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. This closure is NOT executed if an error is thrown, but will be executed with a `.Failure` result if an error occurs asynchronously.
- returns: an `NSProgress` instance if a migration has started, or `nil` is no migrations are required
*/
public static func addSQLiteStore(fileName fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle]? = nil, completion: (PersistentStoreResult) -> Void) throws -> NSProgress? {
return try self.defaultStack.addSQLiteStore(
fileName: fileName,
configuration: configuration,
mappingModelBundles: mappingModelBundles,
completion: completion
)
}
/**
Asynchronously adds to the `defaultStack` 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`.)
- 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 mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`.
- 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. This closure is NOT executed if an error is thrown, but will be executed with a `.Failure` result if an error occurs asynchronously.
- returns: an `NSProgress` instance if a migration has started, or `nil` is no migrations are required
*/
public static func addSQLiteStore(fileURL fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, mappingModelBundles: [NSBundle]? = NSBundle.allBundles(), completion: (PersistentStoreResult) -> Void) throws -> NSProgress? {
return try self.defaultStack.addSQLiteStore(
fileURL: fileURL,
configuration: configuration,
mappingModelBundles: mappingModelBundles,
completion: completion
)
}
/**
Using the `defaultStack`, migrates an SQLite store with the specified filename to the `DataStack`'s managed object model version WITHOUT adding the migrated store to the data stack.
- 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.mainBundle()`.
- parameter sourceBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`.
- returns: an `NSProgress` instance if a migration has started, or `nil` is no migrations are required
*/
public static func upgradeSQLiteStoreIfNeeded(fileName fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle]? = nil, completion: (MigrationResult) -> Void) throws -> NSProgress? {
return try self.defaultStack.upgradeSQLiteStoreIfNeeded(
fileName: fileName,
configuration: configuration,
mappingModelBundles: mappingModelBundles,
completion: completion
)
}
/**
Using the `defaultStack`, 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.
- 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.mainBundle()`.
- parameter sourceBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`.
- returns: an `NSProgress` instance if a migration has started, or `nil` is no migrations are required
*/
public static func upgradeSQLiteStoreIfNeeded(fileURL fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, mappingModelBundles: [NSBundle]? = nil, completion: (MigrationResult) -> Void) throws -> NSProgress? {
return try self.defaultStack.upgradeSQLiteStoreIfNeeded(
fileURL: fileURL,
configuration: configuration,
mappingModelBundles: mappingModelBundles,
completion: completion
)
}
/**
Using the `defaultStack`, checks for the required migrations needed for the store with the specified filename and configuration to be migrated to the `DataStack`'s managed object model version. This method throws an error if the store does not exist, if inspection of the store failed, or no mapping model was found/inferred.
- 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: an array of `MigrationType`s indicating the chain of migrations 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 static func requiredMigrationsForSQLiteStore(fileName fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as [NSBundle]) throws -> [MigrationType] {
return try self.defaultStack.requiredMigrationsForSQLiteStore(
fileName: fileName,
configuration: configuration,
mappingModelBundles: mappingModelBundles
)
}
/**
Using the `defaultStack`, checks if the store at the specified file URL and configuration needs to be migrated to the `DataStack`'s managed object model version.
- 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 static func requiredMigrationsForSQLiteStore(fileURL fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as [NSBundle]) throws -> [MigrationType] {
return try self.defaultStack.requiredMigrationsForSQLiteStore(
fileURL: fileURL,
configuration: configuration,
mappingModelBundles: mappingModelBundles
)
}
}

View File

@@ -38,7 +38,7 @@ public extension DataStack {
- 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 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 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 mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`. - parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`.
- 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. If an error is thrown, this closure will not be executed. - 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. This closure is NOT executed if an error is thrown, but will be executed with a `.Failure` result if an error occurs asynchronously.
- returns: an `NSProgress` instance if a migration has started, or `nil` is no migrations are required - returns: an `NSProgress` instance if a migration has started, or `nil` is no migrations are required
*/ */
public func addSQLiteStore(fileName fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle]? = nil, completion: (PersistentStoreResult) -> Void) throws -> NSProgress? { public func addSQLiteStore(fileName fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle]? = nil, completion: (PersistentStoreResult) -> Void) throws -> NSProgress? {
@@ -60,7 +60,7 @@ public extension DataStack {
- 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 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 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 mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`. - parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`.
- 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. - 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. This closure is NOT executed if an error is thrown, but will be executed with a `.Failure` result if an error occurs asynchronously.
- returns: an `NSProgress` instance if a migration has started, or `nil` is no migrations are required - returns: an `NSProgress` instance if a migration has started, or `nil` is no migrations are required
*/ */
public func addSQLiteStore(fileURL fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, mappingModelBundles: [NSBundle]? = NSBundle.allBundles(), completion: (PersistentStoreResult) -> Void) throws -> NSProgress? { public func addSQLiteStore(fileURL fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, mappingModelBundles: [NSBundle]? = NSBundle.allBundles(), completion: (PersistentStoreResult) -> Void) throws -> NSProgress? {
@@ -124,28 +124,38 @@ public extension DataStack {
return return
} }
let persistentStoreResult = self.addSQLiteStoreAndWait( do {
fileURL: fileURL,
configuration: configuration,
automigrating: false,
resetStoreOnMigrationFailure: false
)
completion(persistentStoreResult) let store = try self.addSQLiteStoreAndWait(
fileURL: fileURL,
configuration: configuration,
automigrating: false,
resetStoreOnMigrationFailure: false
)
completion(PersistentStoreResult(store))
}
catch {
completion(PersistentStoreResult(error as NSError))
}
} }
) )
} }
catch let error as NSError catch let error as NSError
where error.code == NSFileReadNoSuchFileError && error.domain == NSCocoaErrorDomain { where error.code == NSFileReadNoSuchFileError && error.domain == NSCocoaErrorDomain {
let persistentStoreResult = self.addSQLiteStoreAndWait( let store = try self.addSQLiteStoreAndWait(
fileURL: fileURL, fileURL: fileURL,
configuration: configuration, configuration: configuration,
automigrating: false, automigrating: false,
resetStoreOnMigrationFailure: false resetStoreOnMigrationFailure: false
) )
completion(persistentStoreResult) GCDQueue.Main.async {
completion(PersistentStoreResult(store))
}
return nil return nil
} }
catch { catch {
@@ -312,12 +322,16 @@ public extension DataStack {
var operations = [NSOperation]() var operations = [NSOperation]()
var cancelled = false var cancelled = false
let progress = NSProgress(totalUnitCount: numberOfMigrations) let progress = NSProgress(parent: nil, userInfo: nil)
progress.totalUnitCount = numberOfMigrations
// todo nsprogress crashing sometimes
for (sourceModel, destinationModel, mappingModel, _) in migrationSteps { for (sourceModel, destinationModel, mappingModel, _) in migrationSteps {
progress.becomeCurrentWithPendingUnitCount(1) progress.becomeCurrentWithPendingUnitCount(1)
let childProgress = NSProgress(totalUnitCount: 100)
let childProgress = NSProgress(parent: progress, userInfo: nil)
childProgress.totalUnitCount = 100
operations.append( operations.append(
NSBlockOperation { [weak self] in NSBlockOperation { [weak self] in
@@ -338,7 +352,6 @@ public extension DataStack {
mappingModel: mappingModel, mappingModel: mappingModel,
progress: childProgress progress: childProgress
) )
childProgress.setProgressHandler(nil)
} }
catch { catch {
@@ -365,9 +378,8 @@ public extension DataStack {
GCDQueue.Main.async { GCDQueue.Main.async {
progress.setProgressHandler(nil)
completion(migrationResult ?? MigrationResult(migrationTypes)) completion(migrationResult ?? MigrationResult(migrationTypes))
withExtendedLifetime(progress) { (_: NSProgress) -> Void in }
return return
} }
} }

View File

@@ -35,12 +35,12 @@ public extension CoreStore {
/** /**
Adds an in-memory store to the `defaultStack`. Adds an in-memory store to the `defaultStack`.
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to nil. - parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil`.
- returns: a `PersistentStoreResult` indicating success or failure. - returns: the `NSPersistentStore` added to the stack.
*/ */
public static func addInMemoryStore(configuration: String? = nil) -> PersistentStoreResult { public static func addInMemoryStore(configuration configuration: String? = nil) throws -> NSPersistentStore {
return self.defaultStack.addInMemoryStore(configuration: configuration) return try self.defaultStack.addInMemoryStore(configuration: configuration)
} }
/** /**
@@ -50,11 +50,11 @@ public extension CoreStore {
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to nil. - parameter configuration: an optional configuration name from the model file. If not specified, defaults to nil.
- parameter automigrating: Set to true to configure Core Data auto-migration, or false to disable. If not specified, defaults to true. - 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 - 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. - returns: the `NSPersistentStore` added to the stack.
*/ */
public static func addSQLiteStoreAndWait(fileName fileName: String, configuration: String? = nil, automigrating: Bool = true, resetStoreOnMigrationFailure: Bool = false) -> PersistentStoreResult { public static func addSQLiteStoreAndWait(fileName fileName: String, configuration: String? = nil, automigrating: Bool = true, resetStoreOnMigrationFailure: Bool = false) throws -> NSPersistentStore {
return self.defaultStack.addSQLiteStoreAndWait( return try self.defaultStack.addSQLiteStoreAndWait(
fileName: fileName, fileName: fileName,
configuration: configuration, configuration: configuration,
automigrating: automigrating, automigrating: automigrating,
@@ -69,16 +69,15 @@ public extension CoreStore {
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to nil. - parameter configuration: an optional configuration name from the model file. If not specified, defaults to nil.
- parameter automigrating: Set to true to configure Core Data auto-migration, or false to disable. If not specified, defaults to true. - 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. - 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. - returns: the `NSPersistentStore` added to the stack.
*/ */
public static func addSQLiteStoreAndWait(fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, automigrating: Bool = true, resetStoreOnMigrationFailure: Bool = false) -> PersistentStoreResult { public static func addSQLiteStoreAndWait(fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, automigrating: Bool = true, resetStoreOnMigrationFailure: Bool = false) throws -> NSPersistentStore {
return self.defaultStack.addSQLiteStoreAndWait( return try self.defaultStack.addSQLiteStoreAndWait(
fileURL: fileURL, fileURL: fileURL,
configuration: configuration, configuration: configuration,
automigrating: automigrating, automigrating: automigrating,
resetStoreOnMigrationFailure: resetStoreOnMigrationFailure resetStoreOnMigrationFailure: resetStoreOnMigrationFailure
) )
} }
} }

View File

@@ -67,7 +67,6 @@ public final class DataStack {
self.coordinator = NSPersistentStoreCoordinator(managedObjectModel: model) self.coordinator = NSPersistentStoreCoordinator(managedObjectModel: model)
self.rootSavingContext = NSManagedObjectContext.rootSavingContextForCoordinator(self.coordinator) self.rootSavingContext = NSManagedObjectContext.rootSavingContextForCoordinator(self.coordinator)
self.mainContext = NSManagedObjectContext.mainContextForRootContext(self.rootSavingContext) self.mainContext = NSManagedObjectContext.mainContextForRootContext(self.rootSavingContext)
self.bundle = bundle
self.model = model self.model = model
self.migrationChain = migrationChain self.migrationChain = migrationChain
@@ -75,7 +74,7 @@ public final class DataStack {
} }
/** /**
Returns the `DataStack`'s model version. The version string is the same as the name of the .xcdatamodeld file. Returns the `DataStack`'s model version. The version string is the same as the name of the version-specific .xcdatamodeld file.
*/ */
public var modelVersion: String { public var modelVersion: String {
@@ -85,10 +84,10 @@ public final class DataStack {
/** /**
Adds an in-memory store to the stack. Adds an in-memory store to the stack.
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to nil. - parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil`.
- returns: a `PersistentStoreResult` indicating success or failure. - returns: the `NSPersistentStore` added to the stack.
*/ */
public func addInMemoryStore(configuration configuration: String? = nil) -> PersistentStoreResult { public func addInMemoryStore(configuration configuration: String? = nil) throws -> NSPersistentStore {
let coordinator = self.coordinator; let coordinator = self.coordinator;
@@ -114,25 +113,15 @@ public final class DataStack {
if let store = store { if let store = store {
self.updateMetadataForPersistentStore(store) self.updateMetadataForPersistentStore(store)
return PersistentStoreResult(store) return store
} }
if let error = storeError { let error = storeError ?? NSError(coreStoreErrorCode: .UnknownError)
CoreStore.handleError(
CoreStore.handleError( error,
error, "Failed to add in-memory \(typeName(NSPersistentStore)) to the stack."
"Failed to add in-memory \(typeName(NSPersistentStore))." )
) throw error
return PersistentStoreResult(error)
}
else {
CoreStore.handleError(
NSError(coreStoreErrorCode: .UnknownError),
"Failed to add in-memory \(typeName(NSPersistentStore))."
)
return PersistentStoreResult(.UnknownError)
}
} }
/** /**
@@ -142,11 +131,11 @@ public final class DataStack {
- 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 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 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 - 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. - returns: the `NSPersistentStore` added to the stack.
*/ */
public func addSQLiteStoreAndWait(fileName fileName: String, configuration: String? = nil, automigrating: Bool = true, resetStoreOnMigrationFailure: Bool = false) -> PersistentStoreResult { public func addSQLiteStoreAndWait(fileName fileName: String, configuration: String? = nil, automigrating: Bool = true, resetStoreOnMigrationFailure: Bool = false) throws -> NSPersistentStore {
return self.addSQLiteStoreAndWait( return try self.addSQLiteStoreAndWait(
fileURL: applicationSupportDirectory.URLByAppendingPathComponent( fileURL: applicationSupportDirectory.URLByAppendingPathComponent(
fileName, fileName,
isDirectory: false isDirectory: false
@@ -164,9 +153,9 @@ public final class DataStack {
- 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 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 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. - 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. - returns: the `NSPersistentStore` added to the stack.
*/ */
public func addSQLiteStoreAndWait(fileURL 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) throws -> NSPersistentStore {
CoreStore.assert( CoreStore.assert(
fileURL.fileURL, fileURL.fileURL,
@@ -182,15 +171,16 @@ public final class DataStack {
&& isExistingStoreAutomigrating == automigrating && isExistingStoreAutomigrating == automigrating
&& store.configurationName == (configuration ?? Into.defaultConfigurationName) { && store.configurationName == (configuration ?? Into.defaultConfigurationName) {
return PersistentStoreResult(store) return store
} }
let error = NSError(coreStoreErrorCode: .DifferentPersistentStoreExistsAtURL)
CoreStore.handleError( CoreStore.handleError(
NSError(coreStoreErrorCode: .DifferentPersistentStoreExistsAtURL), error,
"Failed to add SQLite \(typeName(NSPersistentStore)) at \"\(fileURL)\" because a different \(typeName(NSPersistentStore)) 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) throw error
} }
let fileManager = NSFileManager.defaultManager() let fileManager = NSFileManager.defaultManager()
@@ -230,7 +220,7 @@ public final class DataStack {
if let store = store { if let store = store {
self.updateMetadataForPersistentStore(store) self.updateMetadataForPersistentStore(store)
return PersistentStoreResult(store) return store
} }
if let error = storeError if let error = storeError
@@ -285,16 +275,16 @@ public final class DataStack {
if let store = store { if let store = store {
self.updateMetadataForPersistentStore(store) self.updateMetadataForPersistentStore(store)
return PersistentStoreResult(store) return store
} }
} }
let error = storeError ?? NSError(coreStoreErrorCode: .UnknownError)
CoreStore.handleError( CoreStore.handleError(
storeError ?? NSError(coreStoreErrorCode: .UnknownError), error,
"Failed to add SQLite \(typeName(NSPersistentStore)) at \"\(fileURL)\"." "Failed to add SQLite \(typeName(NSPersistentStore)) at \"\(fileURL)\"."
) )
throw error
return PersistentStoreResult(.UnknownError)
} }
@@ -303,7 +293,6 @@ public final class DataStack {
internal let coordinator: NSPersistentStoreCoordinator internal let coordinator: NSPersistentStoreCoordinator
internal let rootSavingContext: NSManagedObjectContext internal let rootSavingContext: NSManagedObjectContext
internal let mainContext: NSManagedObjectContext internal let mainContext: NSManagedObjectContext
internal let bundle: NSBundle
internal let model: NSManagedObjectModel internal let model: NSManagedObjectModel
internal let migrationChain: MigrationChain internal let migrationChain: MigrationChain
internal let childTransactionQueue: GCDQueue = .createSerial("com.corestore.datastack.childtransactionqueue") internal let childTransactionQueue: GCDQueue = .createSerial("com.corestore.datastack.childtransactionqueue")

View File

@@ -30,26 +30,28 @@ import CoreData
// MARK: - PersistentStoreResult // MARK: - PersistentStoreResult
/** /**
The `PersistentStoreResult` indicates the result of initializing the persistent store. The `PersistentStoreResult` indicates the result of an asynchronous initialization of a persistent store.
The `PersistentStoreResult` can be treated as a boolean: The `PersistentStoreResult` can be treated as a boolean:
let result = CoreStore.addSQLiteStoreAndWait() try! CoreStore.addSQLiteStore(completion: { (result: PersistentStoreResult) -> Void in
if result { if result {
// succeeded // succeeded
} }
else { else {
// failed // failed
} }
})
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:
let result = CoreStore.addSQLiteStoreAndWait() try! CoreStore.addSQLiteStore(completion: { (result: PersistentStoreResult) -> Void in
switch result { switch result {
case .Success(let persistentStore): case .Success(let persistentStore):
// persistentStore is the related NSPersistentStore instance // persistentStore is the related NSPersistentStore instance
case .Failure(let error): case .Failure(let error):
// error is the NSError instance for the failure // error is the NSError instance for the failure
} }
})
``` ```
*/ */
public enum PersistentStoreResult { public enum PersistentStoreResult {

View File

@@ -11,7 +11,8 @@
B503FAE01AFDC71700F90881 /* ObjectObserverDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B503FADC1AFDC71700F90881 /* ObjectObserverDemoViewController.swift */; }; B503FAE01AFDC71700F90881 /* ObjectObserverDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B503FADC1AFDC71700F90881 /* ObjectObserverDemoViewController.swift */; };
B503FAE11AFDC71700F90881 /* Palette.swift in Sources */ = {isa = PBXBuildFile; fileRef = B503FADD1AFDC71700F90881 /* Palette.swift */; }; B503FAE11AFDC71700F90881 /* Palette.swift in Sources */ = {isa = PBXBuildFile; fileRef = B503FADD1AFDC71700F90881 /* Palette.swift */; };
B503FAE21AFDC71700F90881 /* PaletteTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B503FADE1AFDC71700F90881 /* PaletteTableViewCell.swift */; }; B503FAE21AFDC71700F90881 /* PaletteTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B503FADE1AFDC71700F90881 /* PaletteTableViewCell.swift */; };
B50D67911B4BCFED00124277 /* OrganismV3ToV2.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = B50D67901B4BCFED00124277 /* OrganismV3ToV2.xcmappingmodel */; }; B5125C121B521B78003A42C7 /* OrganismV2ToV3.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = B5125C111B521B78003A42C7 /* OrganismV2ToV3.xcmappingmodel */; };
B5125C141B521BA7003A42C7 /* OrganismV3ToV2.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = B5125C131B521BA7003A42C7 /* OrganismV3ToV2.xcmappingmodel */; };
B52977D91B120B80003D50A5 /* ObserversViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52977D81B120B80003D50A5 /* ObserversViewController.swift */; }; B52977D91B120B80003D50A5 /* ObserversViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52977D81B120B80003D50A5 /* ObserversViewController.swift */; };
B52977DD1B120F3B003D50A5 /* TransactionsDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52977DC1B120F3B003D50A5 /* TransactionsDemoViewController.swift */; }; B52977DD1B120F3B003D50A5 /* TransactionsDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52977DC1B120F3B003D50A5 /* TransactionsDemoViewController.swift */; };
B52977DF1B120F83003D50A5 /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B52977DE1B120F83003D50A5 /* MapKit.framework */; }; B52977DF1B120F83003D50A5 /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B52977DE1B120F83003D50A5 /* MapKit.framework */; };
@@ -38,12 +39,12 @@
B583A9211AF5F542001F76AF /* CoreStore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B583A91B1AF5F4F4001F76AF /* CoreStore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; B583A9211AF5F542001F76AF /* CoreStore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B583A91B1AF5F4F4001F76AF /* CoreStore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
B5D9C9191B20AB1900E64F0E /* GCDKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5D9C9181B20AB1900E64F0E /* GCDKit.framework */; }; B5D9C9191B20AB1900E64F0E /* GCDKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5D9C9181B20AB1900E64F0E /* GCDKit.framework */; };
B5D9C91A1B20AB1900E64F0E /* GCDKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B5D9C9181B20AB1900E64F0E /* GCDKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; B5D9C91A1B20AB1900E64F0E /* GCDKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B5D9C9181B20AB1900E64F0E /* GCDKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
B5E599321B5240F50084BD5F /* OrganismTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E599311B5240F50084BD5F /* OrganismTableViewCell.swift */; };
B5EE25851B36E23C0000406B /* OrganismV1.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EE25841B36E23C0000406B /* OrganismV1.swift */; }; B5EE25851B36E23C0000406B /* OrganismV1.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EE25841B36E23C0000406B /* OrganismV1.swift */; };
B5EE25871B36E2520000406B /* OrganismV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EE25861B36E2520000406B /* OrganismV2.swift */; }; B5EE25871B36E2520000406B /* OrganismV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EE25861B36E2520000406B /* OrganismV2.swift */; };
B5EE258C1B36E40D0000406B /* MigrationsDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EE258B1B36E40D0000406B /* MigrationsDemoViewController.swift */; }; B5EE258C1B36E40D0000406B /* MigrationsDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EE258B1B36E40D0000406B /* MigrationsDemoViewController.swift */; };
B5EE259B1B3EA4890000406B /* OrganismV3.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EE259A1B3EA4890000406B /* OrganismV3.swift */; }; B5EE259B1B3EA4890000406B /* OrganismV3.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EE259A1B3EA4890000406B /* OrganismV3.swift */; };
B5EE259E1B3EC1B20000406B /* OrganismProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EE259D1B3EC1B20000406B /* OrganismProtocol.swift */; }; B5EE259E1B3EC1B20000406B /* OrganismProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EE259D1B3EC1B20000406B /* OrganismProtocol.swift */; };
B5F45A611B4AE5A700831F2F /* OrganismV2ToV3.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = B5F45A601B4AE5A700831F2F /* OrganismV2ToV3.xcmappingmodel */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@@ -97,7 +98,8 @@
B503FADC1AFDC71700F90881 /* ObjectObserverDemoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjectObserverDemoViewController.swift; sourceTree = "<group>"; }; B503FADC1AFDC71700F90881 /* ObjectObserverDemoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjectObserverDemoViewController.swift; sourceTree = "<group>"; };
B503FADD1AFDC71700F90881 /* Palette.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Palette.swift; sourceTree = "<group>"; }; B503FADD1AFDC71700F90881 /* Palette.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Palette.swift; sourceTree = "<group>"; };
B503FADE1AFDC71700F90881 /* PaletteTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaletteTableViewCell.swift; sourceTree = "<group>"; }; B503FADE1AFDC71700F90881 /* PaletteTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaletteTableViewCell.swift; sourceTree = "<group>"; };
B50D67901B4BCFED00124277 /* OrganismV3ToV2.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; name = OrganismV3ToV2.xcmappingmodel; path = "CoreStoreDemo/MIgrations Demo/OrganismV3ToV2.xcmappingmodel"; sourceTree = SOURCE_ROOT; }; B5125C111B521B78003A42C7 /* OrganismV2ToV3.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = OrganismV2ToV3.xcmappingmodel; sourceTree = "<group>"; };
B5125C131B521BA7003A42C7 /* OrganismV3ToV2.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = OrganismV3ToV2.xcmappingmodel; sourceTree = "<group>"; };
B52977D81B120B80003D50A5 /* ObserversViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObserversViewController.swift; sourceTree = "<group>"; }; B52977D81B120B80003D50A5 /* ObserversViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObserversViewController.swift; sourceTree = "<group>"; };
B52977DC1B120F3B003D50A5 /* TransactionsDemoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionsDemoViewController.swift; sourceTree = "<group>"; }; B52977DC1B120F3B003D50A5 /* TransactionsDemoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionsDemoViewController.swift; sourceTree = "<group>"; };
B52977DE1B120F83003D50A5 /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = System/Library/Frameworks/MapKit.framework; sourceTree = SDKROOT; }; B52977DE1B120F83003D50A5 /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = System/Library/Frameworks/MapKit.framework; sourceTree = SDKROOT; };
@@ -124,6 +126,7 @@
B56965281B3582D30075EE4A /* MigrationDemo.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MigrationDemo.xcdatamodel; sourceTree = "<group>"; }; B56965281B3582D30075EE4A /* MigrationDemo.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MigrationDemo.xcdatamodel; sourceTree = "<group>"; };
B583A9141AF5F4F3001F76AF /* CoreStore.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CoreStore.xcodeproj; path = ../CoreStore.xcodeproj; sourceTree = "<group>"; }; B583A9141AF5F4F3001F76AF /* CoreStore.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CoreStore.xcodeproj; path = ../CoreStore.xcodeproj; sourceTree = "<group>"; };
B5D9C9181B20AB1900E64F0E /* GCDKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = GCDKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; B5D9C9181B20AB1900E64F0E /* GCDKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = GCDKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B5E599311B5240F50084BD5F /* OrganismTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OrganismTableViewCell.swift; path = "CoreStoreDemo/MIgrations Demo/OrganismTableViewCell.swift"; sourceTree = SOURCE_ROOT; };
B5EE25801B36E1B00000406B /* MigrationDemoV2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MigrationDemoV2.xcdatamodel; sourceTree = "<group>"; }; B5EE25801B36E1B00000406B /* MigrationDemoV2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MigrationDemoV2.xcdatamodel; sourceTree = "<group>"; };
B5EE25841B36E23C0000406B /* OrganismV1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrganismV1.swift; sourceTree = "<group>"; }; B5EE25841B36E23C0000406B /* OrganismV1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrganismV1.swift; sourceTree = "<group>"; };
B5EE25861B36E2520000406B /* OrganismV2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrganismV2.swift; sourceTree = "<group>"; }; B5EE25861B36E2520000406B /* OrganismV2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrganismV2.swift; sourceTree = "<group>"; };
@@ -131,7 +134,6 @@
B5EE258B1B36E40D0000406B /* MigrationsDemoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MigrationsDemoViewController.swift; sourceTree = "<group>"; }; B5EE258B1B36E40D0000406B /* MigrationsDemoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MigrationsDemoViewController.swift; sourceTree = "<group>"; };
B5EE259A1B3EA4890000406B /* OrganismV3.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrganismV3.swift; sourceTree = "<group>"; }; B5EE259A1B3EA4890000406B /* OrganismV3.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrganismV3.swift; sourceTree = "<group>"; };
B5EE259D1B3EC1B20000406B /* OrganismProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrganismProtocol.swift; sourceTree = "<group>"; }; B5EE259D1B3EC1B20000406B /* OrganismProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrganismProtocol.swift; sourceTree = "<group>"; };
B5F45A601B4AE5A700831F2F /* OrganismV2ToV3.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = OrganismV2ToV3.xcmappingmodel; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@@ -265,9 +267,10 @@
B5EE25861B36E2520000406B /* OrganismV2.swift */, B5EE25861B36E2520000406B /* OrganismV2.swift */,
B5EE25841B36E23C0000406B /* OrganismV1.swift */, B5EE25841B36E23C0000406B /* OrganismV1.swift */,
B5EE258B1B36E40D0000406B /* MigrationsDemoViewController.swift */, B5EE258B1B36E40D0000406B /* MigrationsDemoViewController.swift */,
B5F45A601B4AE5A700831F2F /* OrganismV2ToV3.xcmappingmodel */, B5E599311B5240F50084BD5F /* OrganismTableViewCell.swift */,
B5125C111B521B78003A42C7 /* OrganismV2ToV3.xcmappingmodel */,
B560070E1B3EC90F00A9A8F9 /* OrganismV2ToV3MigrationPolicy.swift */, B560070E1B3EC90F00A9A8F9 /* OrganismV2ToV3MigrationPolicy.swift */,
B50D67901B4BCFED00124277 /* OrganismV3ToV2.xcmappingmodel */, B5125C131B521BA7003A42C7 /* OrganismV3ToV2.xcmappingmodel */,
); );
path = "Migrations Demo"; path = "Migrations Demo";
sourceTree = "<group>"; sourceTree = "<group>";
@@ -380,6 +383,7 @@
files = ( files = (
B56965181B2E20CC0075EE4A /* TimeZone.swift in Sources */, B56965181B2E20CC0075EE4A /* TimeZone.swift in Sources */,
B56965291B3582D30075EE4A /* MigrationDemo.xcdatamodeld in Sources */, B56965291B3582D30075EE4A /* MigrationDemo.xcdatamodeld in Sources */,
B5E599321B5240F50084BD5F /* OrganismTableViewCell.swift in Sources */,
B5EE25851B36E23C0000406B /* OrganismV1.swift in Sources */, B5EE25851B36E23C0000406B /* OrganismV1.swift in Sources */,
B52977DD1B120F3B003D50A5 /* TransactionsDemoViewController.swift in Sources */, B52977DD1B120F3B003D50A5 /* TransactionsDemoViewController.swift in Sources */,
B52977E41B121635003D50A5 /* Place.swift in Sources */, B52977E41B121635003D50A5 /* Place.swift in Sources */,
@@ -394,11 +398,10 @@
B566E3321B11DF3200F4F0C6 /* UserAccount.swift in Sources */, B566E3321B11DF3200F4F0C6 /* UserAccount.swift in Sources */,
B54AAD521AF4D26E00848AE0 /* CoreStoreDemo.xcdatamodeld in Sources */, B54AAD521AF4D26E00848AE0 /* CoreStoreDemo.xcdatamodeld in Sources */,
B5EE259B1B3EA4890000406B /* OrganismV3.swift in Sources */, B5EE259B1B3EA4890000406B /* OrganismV3.swift in Sources */,
B5F45A611B4AE5A700831F2F /* OrganismV2ToV3.xcmappingmodel in Sources */,
B503FAE11AFDC71700F90881 /* Palette.swift in Sources */, B503FAE11AFDC71700F90881 /* Palette.swift in Sources */,
B503FAE21AFDC71700F90881 /* PaletteTableViewCell.swift in Sources */, B503FAE21AFDC71700F90881 /* PaletteTableViewCell.swift in Sources */,
B50D67911B4BCFED00124277 /* OrganismV3ToV2.xcmappingmodel in Sources */,
B560070F1B3EC90F00A9A8F9 /* OrganismV2ToV3MigrationPolicy.swift in Sources */, B560070F1B3EC90F00A9A8F9 /* OrganismV2ToV3MigrationPolicy.swift in Sources */,
B5125C141B521BA7003A42C7 /* OrganismV3ToV2.xcmappingmodel in Sources */,
B503FADF1AFDC71700F90881 /* ListObserverDemoViewController.swift in Sources */, B503FADF1AFDC71700F90881 /* ListObserverDemoViewController.swift in Sources */,
B54AAD4F1AF4D26E00848AE0 /* AppDelegate.swift in Sources */, B54AAD4F1AF4D26E00848AE0 /* AppDelegate.swift in Sources */,
B56964D71B231AE90075EE4A /* StackSetupDemo.xcdatamodeld in Sources */, B56964D71B231AE90075EE4A /* StackSetupDemo.xcdatamodeld in Sources */,
@@ -406,6 +409,7 @@
B5EE259E1B3EC1B20000406B /* OrganismProtocol.swift in Sources */, B5EE259E1B3EC1B20000406B /* OrganismProtocol.swift in Sources */,
B5EE258C1B36E40D0000406B /* MigrationsDemoViewController.swift in Sources */, B5EE258C1B36E40D0000406B /* MigrationsDemoViewController.swift in Sources */,
B569651C1B30889A0075EE4A /* QueryingResultsViewController.swift in Sources */, B569651C1B30889A0075EE4A /* QueryingResultsViewController.swift in Sources */,
B5125C121B521B78003A42C7 /* OrganismV2ToV3.xcmappingmodel in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

View File

@@ -22,8 +22,7 @@
<string>HelveticaNeue-Light</string> <string>HelveticaNeue-Light</string>
<string>HelveticaNeue-Light</string> <string>HelveticaNeue-Light</string>
<string>HelveticaNeue-Light</string> <string>HelveticaNeue-Light</string>
<string>HelveticaNeue-Light</string> <string>HelveticaNeue-Thin</string>
<string>HelveticaNeue-Light</string>
<string>HelveticaNeue-Light</string> <string>HelveticaNeue-Light</string>
<string>HelveticaNeue-Light</string> <string>HelveticaNeue-Light</string>
<string>HelveticaNeue-Light</string> <string>HelveticaNeue-Light</string>
@@ -44,7 +43,8 @@
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="S3A-lm-AuA"> <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="S3A-lm-AuA">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/> <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> <color key="backgroundColor" red="0.20392156859999999" green="0.28627450980000002" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="separatorColor" red="0.20392156859999999" green="0.28627450980000002" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<view key="tableHeaderView" contentMode="scaleToFill" id="yud-WH-MPa"> <view key="tableHeaderView" contentMode="scaleToFill" id="yud-WH-MPa">
<rect key="frame" x="0.0" y="64" width="600" height="150"/> <rect key="frame" x="0.0" y="64" width="600" height="150"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
@@ -123,118 +123,151 @@
<point key="canvasLocation" x="3694" y="650"/> <point key="canvasLocation" x="3694" y="650"/>
</scene> </scene>
<!--Evolution--> <!--Evolution-->
<scene sceneID="zQS-zk-2T4"> <scene sceneID="iwU-Hv-zNr">
<objects> <objects>
<tableViewController id="hJK-5I-1TQ" customClass="MigrationsDemoViewController" customModule="CoreStoreDemo" customModuleProvider="target" sceneMemberID="viewController"> <viewController automaticallyAdjustsScrollViewInsets="NO" id="iVv-Vc-nCL" customClass="MigrationsDemoViewController" customModule="CoreStoreDemo" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="KpQ-h6-kAe"> <layoutGuides>
<viewControllerLayoutGuide type="top" id="Un6-jI-efh"/>
<viewControllerLayoutGuide type="bottom" id="t9A-zf-Iew"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="75P-2m-6cr">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/> <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.20392156859999999" green="0.28627450980000002" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <subviews>
<color key="separatorColor" red="0.20392156859999999" green="0.28627450980000002" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" translatesAutoresizingMaskIntoConstraints="NO" id="WUc-3Y-Quw">
<view key="tableHeaderView" contentMode="scaleToFill" id="5Hd-jr-1nW"> <rect key="frame" x="0.0" y="324" width="600" height="276"/>
<rect key="frame" x="0.0" y="64" width="600" height="259"/> <color key="backgroundColor" red="0.93725490196078431" green="0.93725490196078431" blue="0.95686274509803926" alpha="1" colorSpace="calibratedRGB"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <color key="separatorColor" red="0.20392156859999999" green="0.28627450980000002" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<subviews> <prototypes>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Organism" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="C06-r4-27K"> <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="OrganismTableViewCell" id="WVb-th-o8c" customClass="OrganismTableViewCell" customModule="CoreStoreDemo" customModuleProvider="target">
<rect key="frame" x="20" y="90" width="560" height="26.5"/> <rect key="frame" x="0.0" y="22" width="600" height="44"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="22"/> <autoresizingMask key="autoresizingMask"/>
<color key="textColor" red="0.92549019610000005" green="0.94117647059999998" blue="0.94509803920000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="WVb-th-o8c" id="JBq-Ml-a9p">
<nil key="highlightedColor"/> <rect key="frame" x="0.0" y="0.0" width="600" height="43.5"/>
</label> <autoresizingMask key="autoresizingMask"/>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="attributes" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fXh-OL-4Qb"> <subviews>
<rect key="frame" x="20" y="140" width="560" height="20.5"/> <button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="OQf-Bd-Zze">
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="17"/> <rect key="frame" x="520" y="8" width="72" height="27.5"/>
<color key="textColor" red="0.92549019610000005" green="0.94117647059999998" blue="0.94509803920000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
<nil key="highlightedColor"/> <inset key="contentEdgeInsets" minX="7" minY="0.0" maxX="7" maxY="0.0"/>
</label> <state key="normal" title="mutate!"/>
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="Hwa-fO-fC5"> <connections>
<rect key="frame" x="20" y="20" width="560" height="29"/> <action selector="mutateButtonTouchUpInside:" destination="WVb-th-o8c" eventType="touchUpInside" id="REw-UX-rJ0"/>
<segments> </connections>
<segment title="First"/> </button>
<segment title="Second"/> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VZk-6K-4ut">
<segment title=""/> <rect key="frame" x="15" y="8" width="495" height="27.5"/>
</segments> <fontDescription key="fontDescription" name="HelveticaNeue-Thin" family="Helvetica Neue" pointSize="17"/>
<color key="tintColor" cocoaTouchSystemColor="tableCellGroupedBackgroundColor"/> <color key="textColor" red="0.20392156859999999" green="0.28627450980000002" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections> <nil key="highlightedColor"/>
<action selector="segmentedControlValueChanged:" destination="hJK-5I-1TQ" eventType="valueChanged" id="jNH-gx-mOg"/> </label>
</connections> </subviews>
</segmentedControl> <constraints>
<progressView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" progress="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="H1P-2g-DHX"> <constraint firstItem="OQf-Bd-Zze" firstAttribute="top" secondItem="JBq-Ml-a9p" secondAttribute="topMargin" id="39z-i5-EeS"/>
<rect key="frame" x="20" y="68" width="560" height="2"/> <constraint firstItem="VZk-6K-4ut" firstAttribute="top" secondItem="JBq-Ml-a9p" secondAttribute="topMargin" id="WO1-gf-MIu"/>
<color key="progressTintColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/> <constraint firstItem="VZk-6K-4ut" firstAttribute="leading" secondItem="JBq-Ml-a9p" secondAttribute="leadingMargin" constant="7" id="X0N-Il-F26"/>
<color key="trackTintColor" white="1" alpha="0.20000000000000001" colorSpace="calibratedWhite"/> <constraint firstAttribute="bottomMargin" secondItem="OQf-Bd-Zze" secondAttribute="bottom" id="YwC-dw-rGI"/>
</progressView> <constraint firstItem="OQf-Bd-Zze" firstAttribute="leading" secondItem="VZk-6K-4ut" secondAttribute="trailing" constant="10" id="mo1-NZ-iPa"/>
</subviews> <constraint firstAttribute="bottomMargin" secondItem="VZk-6K-4ut" secondAttribute="bottom" id="ucK-FJ-7v4"/>
<color key="backgroundColor" red="0.20392156859999999" green="0.28627450980000002" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <constraint firstItem="OQf-Bd-Zze" firstAttribute="trailing" secondItem="JBq-Ml-a9p" secondAttribute="trailingMargin" id="v3e-PY-3wW"/>
<constraints> </constraints>
<constraint firstItem="H1P-2g-DHX" firstAttribute="leading" secondItem="5Hd-jr-1nW" secondAttribute="leading" constant="20" id="2Nf-U0-bTq"/> </tableViewCellContentView>
<constraint firstItem="fXh-OL-4Qb" firstAttribute="top" secondItem="C06-r4-27K" secondAttribute="bottom" constant="23.5" id="6d1-A5-Zc5"/> <connections>
<constraint firstItem="Hwa-fO-fC5" firstAttribute="leading" secondItem="5Hd-jr-1nW" secondAttribute="leading" constant="20" id="CDZ-qV-3eK"/> <outlet property="dnaLabel" destination="VZk-6K-4ut" id="CSU-Ig-qMu"/>
<constraint firstItem="H1P-2g-DHX" firstAttribute="top" secondItem="Hwa-fO-fC5" secondAttribute="bottom" constant="20" id="Dcs-Vv-Xfv"/> <outlet property="mutateButton" destination="OQf-Bd-Zze" id="5gd-Jx-tMT"/>
<constraint firstItem="fXh-OL-4Qb" firstAttribute="leading" secondItem="C06-r4-27K" secondAttribute="leading" id="SnQ-Ra-KkU"/> </connections>
<constraint firstItem="Hwa-fO-fC5" firstAttribute="top" secondItem="5Hd-jr-1nW" secondAttribute="top" constant="20" id="Zo1-kW-kAw"/> </tableViewCell>
<constraint firstItem="C06-r4-27K" firstAttribute="top" secondItem="H1P-2g-DHX" secondAttribute="bottom" constant="20" id="bBW-Bn-oeN"/> </prototypes>
<constraint firstAttribute="trailing" secondItem="fXh-OL-4Qb" secondAttribute="trailing" constant="20" id="mhR-0c-i0n"/> <connections>
<constraint firstAttribute="trailing" secondItem="C06-r4-27K" secondAttribute="trailing" constant="20" id="s0c-XG-e70"/> <outlet property="dataSource" destination="iVv-Vc-nCL" id="yp2-X4-fhE"/>
<constraint firstAttribute="trailing" secondItem="H1P-2g-DHX" secondAttribute="trailing" constant="20" id="swp-18-f5e"/> <outlet property="delegate" destination="iVv-Vc-nCL" id="V5b-s8-XOl"/>
<constraint firstItem="C06-r4-27K" firstAttribute="top" secondItem="Hwa-fO-fC5" secondAttribute="bottom" constant="20" id="tYt-wb-Lk8"/> </connections>
<constraint firstAttribute="trailing" secondItem="Hwa-fO-fC5" secondAttribute="trailing" constant="20" id="ujX-gH-Wlm"/> </tableView>
<constraint firstItem="C06-r4-27K" firstAttribute="leading" secondItem="5Hd-jr-1nW" secondAttribute="leading" constant="20" id="yiG-c9-BZg"/> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XKA-Ub-c2X">
</constraints> <rect key="frame" x="0.0" y="64" width="600" height="260"/>
<variation key="default"> <subviews>
<mask key="constraints"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="i7U-bW-juB" customClass="UIButton">
<exclude reference="tYt-wb-Lk8"/> <rect key="frame" x="0.0" y="0.0" width="600" height="260"/>
</mask> <subviews>
</variation> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Organism" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zxy-nY-P44">
</view> <rect key="frame" x="20" y="90.5" width="560" height="26.5"/>
<prototypes> <fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="22"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="UITableViewCell" textLabel="VMh-dq-EWk" detailTextLabel="HIj-ej-FpM" style="IBUITableViewCellStyleValue1" id="hoX-F9-vzZ"> <color key="textColor" red="0.92549019610000005" green="0.94117647059999998" blue="0.94509803920000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<rect key="frame" x="0.0" y="345" width="600" height="44"/> <nil key="highlightedColor"/>
<autoresizingMask key="autoresizingMask"/> </label>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="hoX-F9-vzZ" id="9ru-7C-dsE"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="attributes" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6Ac-xl-ldZ">
<rect key="frame" x="0.0" y="0.0" width="600" height="43.5"/> <rect key="frame" x="20" y="131.5" width="560" height="20.5"/>
<autoresizingMask key="autoresizingMask"/> <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="17"/>
<subviews> <color key="textColor" red="0.92549019610000005" green="0.94117647059999998" blue="0.94509803920000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="VMh-dq-EWk"> <nil key="highlightedColor"/>
<rect key="frame" x="15" y="13" width="28.5" height="19"/> </label>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="rAZ-eJ-sxy">
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="16"/> <rect key="frame" x="20" y="20" width="560" height="29"/>
<color key="textColor" red="0.17254901959999999" green="0.24313725489999999" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <segments>
<nil key="highlightedColor"/> <segment title="First"/>
</label> <segment title="Second"/>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="HIj-ej-FpM"> <segment title=""/>
<rect key="frame" x="546.5" y="13" width="38.5" height="19"/> </segments>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="16"/> <connections>
<color key="textColor" red="0.5568627451" green="0.5568627451" blue="0.57647058819999997" alpha="1" colorSpace="calibratedRGB"/> <action selector="segmentedControlValueChanged:" destination="iVv-Vc-nCL" eventType="valueChanged" id="RwG-kW-RPg"/>
<nil key="highlightedColor"/> </connections>
</label> </segmentedControl>
</subviews> <progressView opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" verticalHuggingPriority="750" progress="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="869-wx-Odb">
</tableViewCellContentView> <rect key="frame" x="20" y="68" width="560" height="2"/>
</tableViewCell> <color key="progressTintColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
</prototypes> <color key="trackTintColor" white="1" alpha="0.20000000000000001" colorSpace="calibratedWhite"/>
<connections> </progressView>
<outlet property="dataSource" destination="hJK-5I-1TQ" id="PBz-yT-Uix"/> </subviews>
<outlet property="delegate" destination="hJK-5I-1TQ" id="eZs-Fj-9qK"/> <color key="backgroundColor" red="0.20392156859999999" green="0.28627450980000002" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</connections> <constraints>
</tableView> <constraint firstItem="zxy-nY-P44" firstAttribute="leading" secondItem="i7U-bW-juB" secondAttribute="leading" constant="20" id="1u1-Tq-hRn"/>
<toolbarItems/> <constraint firstItem="6Ac-xl-ldZ" firstAttribute="top" secondItem="zxy-nY-P44" secondAttribute="bottom" constant="14.5" id="39B-9l-O3g"/>
<navigationItem key="navigationItem" title="Evolution" id="KvN-Gf-ETO"> <constraint firstAttribute="trailing" secondItem="zxy-nY-P44" secondAttribute="trailing" constant="20" id="3d5-Zk-rB1"/>
<barButtonItem key="rightBarButtonItem" title="Mutate!" id="J3K-Fc-5yB"> <constraint firstAttribute="trailing" secondItem="6Ac-xl-ldZ" secondAttribute="trailing" constant="20" id="EJq-uA-8dQ"/>
<connections> <constraint firstItem="rAZ-eJ-sxy" firstAttribute="top" secondItem="i7U-bW-juB" secondAttribute="top" constant="20" id="Ey7-mP-RNA"/>
<action selector="mutateBarButtonTapped:" destination="hJK-5I-1TQ" id="rPl-0R-hKM"/> <constraint firstItem="zxy-nY-P44" firstAttribute="top" secondItem="869-wx-Odb" secondAttribute="bottom" constant="20.5" id="GU3-JM-b99"/>
</connections> <constraint firstItem="rAZ-eJ-sxy" firstAttribute="leading" secondItem="i7U-bW-juB" secondAttribute="leading" constant="20" id="KKJ-gG-pFL"/>
</barButtonItem> <constraint firstAttribute="trailing" secondItem="869-wx-Odb" secondAttribute="trailing" constant="20" id="Lni-gD-7h0"/>
</navigationItem> <constraint firstItem="869-wx-Odb" firstAttribute="top" secondItem="rAZ-eJ-sxy" secondAttribute="bottom" constant="20" id="QBz-cP-SVZ"/>
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/> <constraint firstAttribute="trailing" secondItem="rAZ-eJ-sxy" secondAttribute="trailing" constant="20" id="WoD-cr-aj9"/>
<constraint firstAttribute="height" constant="260" id="XTa-ql-yEW"/>
<constraint firstItem="6Ac-xl-ldZ" firstAttribute="leading" secondItem="i7U-bW-juB" secondAttribute="leading" constant="20" id="jkv-ow-f0q"/>
<constraint firstItem="869-wx-Odb" firstAttribute="leading" secondItem="i7U-bW-juB" secondAttribute="leading" constant="20" id="qgE-y5-uh8"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" constant="260" id="90R-Mf-iAB"/>
<constraint firstItem="i7U-bW-juB" firstAttribute="leading" secondItem="XKA-Ub-c2X" secondAttribute="leading" id="e9h-f4-c37"/>
<constraint firstItem="i7U-bW-juB" firstAttribute="top" secondItem="XKA-Ub-c2X" secondAttribute="top" id="vJV-dl-RLx"/>
<constraint firstAttribute="trailing" secondItem="i7U-bW-juB" secondAttribute="trailing" id="wYt-hg-yKM"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="XKA-Ub-c2X" secondAttribute="trailing" id="U0P-51-KT9"/>
<constraint firstAttribute="trailing" secondItem="WUc-3Y-Quw" secondAttribute="trailing" id="i7I-7B-97K"/>
<constraint firstItem="WUc-3Y-Quw" firstAttribute="leading" secondItem="75P-2m-6cr" secondAttribute="leading" id="i8b-GU-yFg"/>
<constraint firstItem="XKA-Ub-c2X" firstAttribute="top" secondItem="Un6-jI-efh" secondAttribute="bottom" id="lU2-wP-RQb"/>
<constraint firstItem="XKA-Ub-c2X" firstAttribute="leading" secondItem="75P-2m-6cr" secondAttribute="leading" id="oa7-qv-9am"/>
<constraint firstItem="t9A-zf-Iew" firstAttribute="top" secondItem="WUc-3Y-Quw" secondAttribute="bottom" id="tts-uy-w5J"/>
<constraint firstItem="WUc-3Y-Quw" firstAttribute="top" secondItem="XKA-Ub-c2X" secondAttribute="bottom" id="vjy-iN-Xi7"/>
</constraints>
</view>
<navigationItem key="navigationItem" title="Evolution" id="uc7-DN-PYG"/>
<connections> <connections>
<outlet property="organismLabel" destination="fXh-OL-4Qb" id="shX-fK-haw"/> <outlet property="headerContainer" destination="i7U-bW-juB" id="hpf-7a-yBd"/>
<outlet property="progressView" destination="H1P-2g-DHX" id="Asy-fE-gXI"/> <outlet property="organismLabel" destination="6Ac-xl-ldZ" id="pgF-ej-ovy"/>
<outlet property="segmentedControl" destination="Hwa-fO-fC5" id="myL-tO-4ah"/> <outlet property="progressView" destination="869-wx-Odb" id="um3-UM-uOP"/>
<outlet property="titleLabel" destination="C06-r4-27K" id="zQr-YC-moe"/> <outlet property="segmentedControl" destination="rAZ-eJ-sxy" id="K3Q-X2-Jrm"/>
<outlet property="tableView" destination="WUc-3Y-Quw" id="3Ki-v0-lTb"/>
<outlet property="titleLabel" destination="zxy-nY-P44" id="9JO-hP-yVp"/>
</connections> </connections>
</tableViewController> </viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="H1w-4k-Tpx" userLabel="First Responder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="LzB-gZ-6fG" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects> </objects>
<point key="canvasLocation" x="3694" y="4113"/> <point key="canvasLocation" x="3694" y="4113"/>
</scene> </scene>
@@ -408,7 +441,7 @@
</subviews> </subviews>
</tableViewCellContentView> </tableViewCellContentView>
<connections> <connections>
<segue destination="hJK-5I-1TQ" kind="show" id="u2O-8c-DSB"/> <segue destination="iVv-Vc-nCL" kind="show" id="HbO-rU-Qfj"/>
</connections> </connections>
</tableViewCell> </tableViewCell>
</cells> </cells>

View File

@@ -15,7 +15,7 @@ private struct Static {
static let timeZonesStack: DataStack = { static let timeZonesStack: DataStack = {
let dataStack = DataStack() let dataStack = DataStack()
dataStack.addSQLiteStoreAndWait( try! dataStack.addSQLiteStoreAndWait(
fileName: "TimeZoneDemo.sqlite", fileName: "TimeZoneDemo.sqlite",
configuration: "FetchingAndQueryingDemo", configuration: "FetchingAndQueryingDemo",
resetStoreOnMigrationFailure: true resetStoreOnMigrationFailure: true

View File

@@ -14,7 +14,7 @@ private struct Static {
static let palettes: ListMonitor<Palette> = { static let palettes: ListMonitor<Palette> = {
CoreStore.addSQLiteStoreAndWait( try! CoreStore.addSQLiteStoreAndWait(
fileName: "ColorsDemo.sqlite", fileName: "ColorsDemo.sqlite",
configuration: "ObservingDemo", configuration: "ObservingDemo",
resetStoreOnMigrationFailure: true resetStoreOnMigrationFailure: true

View File

@@ -30,7 +30,7 @@ class CustomLoggerViewController: UIViewController, CoreStoreLogger {
super.viewDidLoad() super.viewDidLoad()
self.dataStack.addSQLiteStoreAndWait(fileName: "emptyStore.sqlite") try! self.dataStack.addSQLiteStoreAndWait(fileName: "emptyStore.sqlite")
CoreStore.logger = self CoreStore.logger = self
} }
@@ -109,7 +109,7 @@ class CustomLoggerViewController: UIViewController, CoreStoreLogger {
} }
case .Some(1): case .Some(1):
self.dataStack.addSQLiteStoreAndWait(fileName: "emptyStore.sqlite", configuration: "invalidStore") try! self.dataStack.addSQLiteStoreAndWait(fileName: "emptyStore.sqlite", configuration: "invalidStore")
case .Some(2): case .Some(2):
self.dataStack.beginAsynchronous { (transaction) -> Void in self.dataStack.beginAsynchronous { (transaction) -> Void in

View File

@@ -12,7 +12,7 @@ import CoreStore
// MARK: - MigrationsDemoViewController // MARK: - MigrationsDemoViewController
class MigrationsDemoViewController: UITableViewController { class MigrationsDemoViewController: UIViewController {
// MARK: UIViewController // MARK: UIViewController
@@ -20,10 +20,9 @@ class MigrationsDemoViewController: UITableViewController {
super.viewDidLoad() super.viewDidLoad()
let models = self.models
if let segmentedControl = self.segmentedControl { if let segmentedControl = self.segmentedControl {
for (index, model) in models.enumerate() { for (index, model) in self.models.enumerate() {
segmentedControl.setTitle( segmentedControl.setTitle(
model.label, model.label,
@@ -31,55 +30,38 @@ class MigrationsDemoViewController: UITableViewController {
) )
} }
} }
self.setDataStack(nil, model: nil, scrollToSelection: false)
}
let dataStack = DataStack(modelName: "MigrationDemo") override func viewDidAppear(animated: Bool) {
do {
let migrations = try dataStack.requiredMigrationsForSQLiteStore( super.viewDidAppear(animated)
fileName: "MigrationDemo.sqlite"
)
let storeVersion = migrations.first?.sourceVersion ?? dataStack.modelVersion let modelMetadata = withExtendedLifetime(DataStack(modelName: "MigrationDemo")) {
for model in models { (dataStack: DataStack) -> ModelMetadata in
if model.version == storeVersion { let models = self.models
do {
self.selectModelVersion(model, animated: false) let migrations = try dataStack.requiredMigrationsForSQLiteStore(
return fileName: "MigrationDemo.sqlite"
)
let storeVersion = migrations.first?.sourceVersion ?? dataStack.modelVersion
for model in models {
if model.version == storeVersion {
return model
}
} }
} }
catch _ { }
return models.first!
} }
catch _ { }
self.selectModelVersion(self.models.first!, animated: false) self.selectModelVersion(modelMetadata)
}
// MARK: UITableViewDataSource
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.models.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("UITableViewCell", forIndexPath: indexPath)
cell.textLabel?.text = self.models[indexPath.row].version
return cell
}
// MARK: UITableViewDelegate
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "Model Versions"
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.selectModelVersion(self.models[indexPath.row], animated: true)
} }
@@ -111,28 +93,36 @@ class MigrationsDemoViewController: UITableViewController {
) )
] ]
private var dataStack: DataStack? private var _listMonitor: ListMonitor<NSManagedObject>?
private var organism: NSManagedObject? private var listMonitor: ListMonitor<NSManagedObject>? {
return self._listMonitor
}
private var _dataStack: DataStack?
private var dataStack: DataStack? {
return self._dataStack
}
private var _lastSelectedIndexPath: NSIndexPath?
private var lastSelectedIndexPath: NSIndexPath? {
return self._lastSelectedIndexPath
}
private func setSelectedIndexPath(indexPath: NSIndexPath, scrollToSelection: Bool) {
self._lastSelectedIndexPath = indexPath
self.updateDisplay(reloadData: false, scrollToSelection: scrollToSelection, animated: true)
}
@IBOutlet private dynamic weak var headerContainer: UIView?
@IBOutlet private dynamic weak var titleLabel: UILabel? @IBOutlet private dynamic weak var titleLabel: UILabel?
@IBOutlet private dynamic weak var organismLabel: UILabel? @IBOutlet private dynamic weak var organismLabel: UILabel?
@IBOutlet private dynamic weak var segmentedControl: UISegmentedControl? @IBOutlet private dynamic weak var segmentedControl: UISegmentedControl?
@IBOutlet private dynamic weak var progressView: UIProgressView? @IBOutlet private dynamic weak var progressView: UIProgressView?
@IBOutlet private dynamic weak var tableView: UITableView?
@IBAction private dynamic func mutateBarButtonTapped(sender: AnyObject?) {
if let dataStack = self.dataStack, let organism = self.organism {
dataStack.beginSynchronous { (transaction) -> Void in
let organism = transaction.edit(organism)
(organism as! OrganismProtocol).mutate()
transaction.commit()
}
self.updateDisplayWithCompletion()
}
}
@IBAction private dynamic func segmentedControlValueChanged(sender: AnyObject?) { @IBAction private dynamic func segmentedControlValueChanged(sender: AnyObject?) {
@@ -141,25 +131,24 @@ class MigrationsDemoViewController: UITableViewController {
return return
} }
self.selectModelVersion(self.models[index], animated: true) self.selectModelVersion(self.models[index])
} }
private func selectModelVersion(model: ModelMetadata, animated: Bool) { private func selectModelVersion(model: ModelMetadata) {
if self.organism?.entity.managedObjectClassName == "\(model.entityType)" { if self.dataStack?.modelVersion == model.version {
return return
} }
self.organism = nil self.setDataStack(nil, model: nil, scrollToSelection: false) // explicitly trigger NSPersistentStore cleanup by deallocating the stack
self.dataStack = nil
let dataStack = DataStack( let dataStack = DataStack(
modelName: "MigrationDemo", modelName: "MigrationDemo",
migrationChain: model.migrationChain migrationChain: model.migrationChain
) )
self.setEnabled(false, animated: animated) self.setEnabled(false)
let progress = try! dataStack.addSQLiteStore( let progress = try! dataStack.addSQLiteStore(
fileName: "MigrationDemo.sqlite", fileName: "MigrationDemo.sqlite",
completion: { [weak self] (result) -> Void in completion: { [weak self] (result) -> Void in
@@ -171,57 +160,50 @@ class MigrationsDemoViewController: UITableViewController {
guard case .Success = result else { guard case .Success = result else {
strongSelf.setEnabled(true, animated: animated) strongSelf.setEnabled(true)
return return
} }
strongSelf.dataStack = dataStack strongSelf.setDataStack(dataStack, model: model, scrollToSelection: true)
if let organism = dataStack.fetchOne(From(model.entityType)) {
strongSelf.organism = organism let count = dataStack.queryValue(From(model.entityType), Select<Int>(.Count("dna")))
if count > 0 {
strongSelf.setEnabled(true)
} }
else { else {
dataStack.beginSynchronous { (transaction) -> Void in dataStack.beginAsynchronous { (transaction) -> Void in
for _ in 0 ..< 100000 { for i: Int64 in 1 ..< 10000 {
let organism = transaction.create(Into(model.entityType)) let organism = transaction.create(Into(model.entityType)) as! OrganismProtocol
(organism as! OrganismProtocol).mutate() organism.dna = i
organism.mutate()
} }
transaction.commit() transaction.commit { result -> Void in
self?.setEnabled(true)
}
} }
strongSelf.organism = dataStack.fetchOne(From(model.entityType))!
} }
strongSelf.updateDisplayWithCompletion()
let indexOfModel = strongSelf.models.map { $0.version }.indexOf(model.version)!
strongSelf.tableView.selectRowAtIndexPath(
NSIndexPath(forRow: indexOfModel, inSection: 0),
animated: false,
scrollPosition: .None
)
strongSelf.segmentedControl?.selectedSegmentIndex = indexOfModel
strongSelf.setEnabled(true, animated: animated)
} }
) )
if let progress = progress { if let progress = progress {
self.updateDisplayWithProgress(progress)
progress.setProgressHandler { [weak self] (progress) -> Void in progress.setProgressHandler { [weak self] (progress) -> Void in
self?.updateDisplayWithProgress(progress) self?.reloadTableHeaderWithProgress(progress)
} }
} }
} }
func setEnabled(enabled: Bool, animated: Bool) { private func setEnabled(enabled: Bool) {
UIView.animateKeyframesWithDuration( UIView.animateWithDuration(
animated ? 0.2 : 0, 0.2,
delay: 0, delay: 0,
options: .BeginFromCurrentState, options: .BeginFromCurrentState,
animations: { () -> Void in animations: { () -> Void in
@@ -231,6 +213,8 @@ class MigrationsDemoViewController: UITableViewController {
navigationItem.rightBarButtonItem?.enabled = enabled navigationItem.rightBarButtonItem?.enabled = enabled
navigationItem.hidesBackButton = !enabled navigationItem.hidesBackButton = !enabled
self.segmentedControl?.enabled = enabled
if let tableView = self.tableView { if let tableView = self.tableView {
tableView.alpha = enabled ? 1.0 : 0.5 tableView.alpha = enabled ? 1.0 : 0.5
@@ -241,18 +225,47 @@ class MigrationsDemoViewController: UITableViewController {
) )
} }
func updateDisplayWithProgress(progress: NSProgress) { private func setDataStack(dataStack: DataStack?, model: ModelMetadata?, scrollToSelection: Bool) {
if let dataStack = dataStack, let model = model {
self.segmentedControl?.selectedSegmentIndex = self.models.map { $0.version }.indexOf(model.version)!
self._dataStack = dataStack
let listMonitor = dataStack.monitorList(From(model.entityType), OrderBy(.Descending("dna")))
listMonitor.addObserver(self)
self._listMonitor = listMonitor
if self.lastSelectedIndexPath == nil {
if listMonitor.numberOfObjectsInSection(0) > 0 {
self.setSelectedIndexPath(NSIndexPath(forRow: 0, inSection: 0), scrollToSelection: true)
}
}
}
else {
self.segmentedControl?.selectedSegmentIndex = UISegmentedControlNoSegment
self._dataStack = nil
self._listMonitor = nil
}
self.updateDisplay(reloadData: true, scrollToSelection: scrollToSelection, animated: false)
}
private func reloadTableHeaderWithProgress(progress: NSProgress) {
self.progressView?.setProgress(Float(progress.fractionCompleted), animated: true) self.progressView?.setProgress(Float(progress.fractionCompleted), animated: true)
self.titleLabel?.text = "Migrating: \(progress.localizedDescription)" self.titleLabel?.text = "Migrating: \(progress.localizedDescription)"
self.organismLabel?.text = "Incremental step \(progress.localizedAdditionalDescription)" self.organismLabel?.text = "Incremental step \(progress.localizedAdditionalDescription)"
} }
func updateDisplayWithCompletion() { private func updateDisplay(reloadData reloadData: Bool, scrollToSelection: Bool, animated: Bool) {
var lines = [String]() var lines = [String]()
var organismType = "" var organismType = ""
if let organism = self.organism { if let indexPath = self.lastSelectedIndexPath, let organism = self.listMonitor?[indexPath] {
for property in organism.entity.properties { for property in organism.entity.properties {
@@ -265,6 +278,102 @@ class MigrationsDemoViewController: UITableViewController {
self.titleLabel?.text = organismType self.titleLabel?.text = organismType
self.organismLabel?.text = "\n".join(lines) self.organismLabel?.text = "\n".join(lines)
self.progressView?.progress = 0 self.progressView?.progress = 0
self.tableView.tableHeaderView?.setNeedsLayout()
self.headerContainer?.setNeedsLayout()
guard let tableView = self.tableView else {
return
}
if reloadData {
tableView.reloadData()
}
tableView.layoutIfNeeded()
if let indexPath = self.lastSelectedIndexPath where indexPath.row < tableView.numberOfRowsInSection(0) {
tableView.selectRowAtIndexPath(indexPath,
animated: scrollToSelection && animated,
scrollPosition: scrollToSelection ? .Middle : .None
)
}
}
}
// MARK: - MigrationsDemoViewController: ListObserver
extension MigrationsDemoViewController: ListObserver {
// MARK: ListObserver
func listMonitorWillChange(monitor: ListMonitor<NSManagedObject>) { }
func listMonitorDidChange(monitor: ListMonitor<NSManagedObject>) {
if self.lastSelectedIndexPath == nil && self.listMonitor?.numberOfObjectsInSection(0) > 0 {
self.tableView?.reloadData()
self.setSelectedIndexPath(NSIndexPath(forRow: 0, inSection: 0), scrollToSelection: false)
}
else {
self.updateDisplay(reloadData: true, scrollToSelection: true, animated: true)
}
}
}
// MARK: - MigrationsDemoViewController: UITableViewDataSource, UITableViewDelegate
extension MigrationsDemoViewController: UITableViewDataSource, UITableViewDelegate {
// MARK: UITableViewDataSource
@objc dynamic func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.listMonitor?.numberOfObjectsInSection(0) ?? 0
}
@objc dynamic func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("OrganismTableViewCell", forIndexPath: indexPath) as! OrganismTableViewCell
let dna = (self.listMonitor?[indexPath] as? OrganismProtocol)?.dna.description ?? ""
cell.dnaLabel?.text = "DNA: \(dna)"
cell.mutateButtonHandler = { [weak self] _ -> Void in
guard let strongSelf = self,
let dataStack = strongSelf.dataStack,
let organism = strongSelf.listMonitor?[indexPath] else {
return
}
strongSelf.setSelectedIndexPath(indexPath, scrollToSelection: false)
strongSelf.setEnabled(false)
dataStack.beginAsynchronous { (transaction) -> Void in
let organism = transaction.edit(organism) as! OrganismProtocol
organism.mutate()
transaction.commit { _ -> Void in
self?.setEnabled(true)
}
}
}
return cell
}
// MARK: UITableViewDelegate
@objc dynamic func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.setSelectedIndexPath(indexPath, scrollToSelection: false)
} }
} }

View File

@@ -8,7 +8,9 @@
import Foundation import Foundation
protocol OrganismProtocol { protocol OrganismProtocol: class {
var dna: Int64 { get set }
func mutate() func mutate()
} }

View File

@@ -0,0 +1,22 @@
//
// OrganismTableViewCell.swift
// CoreStoreDemo
//
// Created by John Rommel Estropia on 2015/07/12.
// Copyright © 2015 John Rommel Estropia. All rights reserved.
//
import UIKit
class OrganismTableViewCell: UITableViewCell {
@IBOutlet weak dynamic var dnaLabel: UILabel?
@IBOutlet weak dynamic var mutateButton: UIButton?
var mutateButtonHandler: (() -> Void)?
@IBAction dynamic func mutateButtonTouchUpInside(sender: UIButton?) {
self.mutateButtonHandler?()
}
}

View File

@@ -11,6 +11,7 @@ import CoreData
class OrganismV1: NSManagedObject, OrganismProtocol { class OrganismV1: NSManagedObject, OrganismProtocol {
@NSManaged var dna: Int64
@NSManaged var hasHead: Bool @NSManaged var hasHead: Bool
@NSManaged var hasTail: Bool @NSManaged var hasTail: Bool

View File

@@ -11,6 +11,7 @@ import CoreData
class OrganismV2: NSManagedObject, OrganismProtocol { class OrganismV2: NSManagedObject, OrganismProtocol {
@NSManaged var dna: Int64
@NSManaged var hasHead: Bool @NSManaged var hasHead: Bool
@NSManaged var hasTail: Bool @NSManaged var hasTail: Bool
@NSManaged var numberOfFlippers: Int32 @NSManaged var numberOfFlippers: Int32

File diff suppressed because one or more lines are too long

View File

@@ -11,6 +11,7 @@ import CoreData
class OrganismV3: NSManagedObject, OrganismProtocol { class OrganismV3: NSManagedObject, OrganismProtocol {
@NSManaged var dna: Int64
@NSManaged var hasHead: Bool @NSManaged var hasHead: Bool
@NSManaged var hasTail: Bool @NSManaged var hasTail: Bool
@NSManaged var numberOfLimbs: Int32 @NSManaged var numberOfLimbs: Int32

File diff suppressed because one or more lines are too long

View File

@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="7701" systemVersion="14D136" minimumToolsVersion="Xcode 4.3" macOSVersion="Automatic" iOSVersion="Automatic"> <model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="7701" systemVersion="14E46" minimumToolsVersion="Xcode 4.3">
<entity name="Organism" representedClassName="CoreStoreDemo.OrganismV1" syncable="YES"> <entity name="Organism" representedClassName="CoreStoreDemo.OrganismV1" syncable="YES">
<attribute name="dna" optional="YES" attributeType="Integer 64" syncable="YES"/>
<attribute name="hasHead" optional="YES" attributeType="Boolean" syncable="YES"/> <attribute name="hasHead" optional="YES" attributeType="Boolean" syncable="YES"/>
<attribute name="hasTail" optional="YES" attributeType="Boolean" syncable="YES"/> <attribute name="hasTail" optional="YES" attributeType="Boolean" syncable="YES"/>
</entity> </entity>
<elements> <elements>
<element name="Organism" positionX="-36" positionY="9" width="128" height="75"/> <element name="Organism" positionX="-36" positionY="9" width="128" height="90"/>
</elements> </elements>
</model> </model>

View File

@@ -1,11 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="8118.20" systemVersion="14D136" minimumToolsVersion="Xcode 4.3"> <model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="7701" systemVersion="14E46" minimumToolsVersion="Xcode 4.3">
<entity name="Organism" representedClassName="CoreStoreDemo.OrganismV2" syncable="YES"> <entity name="Organism" representedClassName="CoreStoreDemo.OrganismV2" syncable="YES">
<attribute name="dna" optional="YES" attributeType="Integer 64" syncable="YES"/>
<attribute name="hasHead" attributeType="Boolean" syncable="YES"/> <attribute name="hasHead" attributeType="Boolean" syncable="YES"/>
<attribute name="hasTail" attributeType="Boolean" syncable="YES"/> <attribute name="hasTail" attributeType="Boolean" syncable="YES"/>
<attribute name="numberOfFlippers" attributeType="Integer 32" defaultValueString="0" syncable="YES"/> <attribute name="numberOfFlippers" attributeType="Integer 32" defaultValueString="0" syncable="YES"/>
</entity> </entity>
<elements> <elements>
<element name="Organism" positionX="-36" positionY="9" width="128" height="90"/> <element name="Organism" positionX="-36" positionY="9" width="128" height="105"/>
</elements> </elements>
</model> </model>

View File

@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="8118.20" systemVersion="14D136" minimumToolsVersion="Xcode 4.3"> <model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="7701" systemVersion="14E46" minimumToolsVersion="Xcode 4.3">
<entity name="Organism" representedClassName="CoreStoreDemo.OrganismV3" syncable="YES"> <entity name="Organism" representedClassName="CoreStoreDemo.OrganismV3" syncable="YES">
<attribute name="dna" optional="YES" attributeType="Integer 64" syncable="YES"/>
<attribute name="hasHead" attributeType="Boolean" syncable="YES"/> <attribute name="hasHead" attributeType="Boolean" syncable="YES"/>
<attribute name="hasTail" attributeType="Boolean" syncable="YES"/> <attribute name="hasTail" attributeType="Boolean" syncable="YES"/>
<attribute name="hasVertebrae" attributeType="Boolean" syncable="YES"/> <attribute name="hasVertebrae" attributeType="Boolean" syncable="YES"/>
<attribute name="numberOfLimbs" attributeType="Integer 32" defaultValueString="0" syncable="YES"/> <attribute name="numberOfLimbs" attributeType="Integer 32" defaultValueString="0" syncable="YES"/>
</entity> </entity>
<elements> <elements>
<element name="Organism" positionX="-36" positionY="9" width="128" height="105"/> <element name="Organism" positionX="-36" positionY="9" width="128" height="120"/>
</elements> </elements>
</model> </model>

View File

@@ -18,12 +18,12 @@ private struct Static {
static let facebookStack: DataStack = { static let facebookStack: DataStack = {
let dataStack = DataStack(modelName: "StackSetupDemo") let dataStack = DataStack(modelName: "StackSetupDemo")
dataStack.addSQLiteStoreAndWait( try! dataStack.addSQLiteStoreAndWait(
fileName: "AccountsDemo_FB_Male.sqlite", fileName: "AccountsDemo_FB_Male.sqlite",
configuration: maleConfiguration, configuration: maleConfiguration,
resetStoreOnMigrationFailure: true resetStoreOnMigrationFailure: true
) )
dataStack.addSQLiteStoreAndWait( try! dataStack.addSQLiteStoreAndWait(
fileName: "AccountsDemo_FB_Female.sqlite", fileName: "AccountsDemo_FB_Female.sqlite",
configuration: femaleConfiguration, configuration: femaleConfiguration,
resetStoreOnMigrationFailure: true resetStoreOnMigrationFailure: true
@@ -52,12 +52,12 @@ private struct Static {
static let twitterStack: DataStack = { static let twitterStack: DataStack = {
let dataStack = DataStack(modelName: "StackSetupDemo") let dataStack = DataStack(modelName: "StackSetupDemo")
dataStack.addSQLiteStoreAndWait( try! dataStack.addSQLiteStoreAndWait(
fileName: "AccountsDemo_TW_Male.sqlite", fileName: "AccountsDemo_TW_Male.sqlite",
configuration: maleConfiguration, configuration: maleConfiguration,
resetStoreOnMigrationFailure: true resetStoreOnMigrationFailure: true
) )
dataStack.addSQLiteStoreAndWait( try! dataStack.addSQLiteStoreAndWait(
fileName: "AccountsDemo_TW_Female.sqlite", fileName: "AccountsDemo_TW_Female.sqlite",
configuration: femaleConfiguration, configuration: femaleConfiguration,
resetStoreOnMigrationFailure: true resetStoreOnMigrationFailure: true

View File

@@ -18,7 +18,7 @@ private struct Static {
static let placeController: ObjectMonitor<Place> = { static let placeController: ObjectMonitor<Place> = {
CoreStore.addSQLiteStoreAndWait( try! CoreStore.addSQLiteStoreAndWait(
fileName: "PlaceDemo.sqlite", fileName: "PlaceDemo.sqlite",
configuration: "TransactionsDemo", configuration: "TransactionsDemo",
resetStoreOnMigrationFailure: true resetStoreOnMigrationFailure: true

View File

@@ -47,22 +47,22 @@ class CoreStoreTests: XCTestCase {
CoreStore.defaultStack = stack CoreStore.defaultStack = stack
XCTAssert(CoreStore.defaultStack === stack, "CoreStore.defaultStack === stack") XCTAssert(CoreStore.defaultStack === stack, "CoreStore.defaultStack === stack")
switch stack.addSQLiteStoreAndWait(fileName: "ConfigStore1.sqlite", configuration: "Config1", resetStoreOnMigrationFailure: true){ do {
try stack.addSQLiteStoreAndWait(fileName: "ConfigStore1.sqlite", configuration: "Config1", resetStoreOnMigrationFailure: true)
}
catch let error as NSError {
case .Failure(let error):
XCTFail(error.description) XCTFail(error.description)
default:
break
} }
switch stack.addSQLiteStoreAndWait(fileName: "ConfigStore2.sqlite", configuration: "Config2", resetStoreOnMigrationFailure: true){ do {
try stack.addSQLiteStoreAndWait(fileName: "ConfigStore2.sqlite", configuration: "Config2", resetStoreOnMigrationFailure: true)
}
catch let error as NSError {
case .Failure(let error):
XCTFail(error.description) XCTFail(error.description)
default:
break
} }
let detachedTransaction = CoreStore.beginDetached() let detachedTransaction = CoreStore.beginDetached()

View File

@@ -43,7 +43,12 @@ I wrote this library when Swift was made public, and CoreStore is now a powerhou
Quick-setup: Quick-setup:
```swift ```swift
CoreStore.addSQLiteStoreAndWait(fileName: "MyStore.sqlite") do {
try CoreStore.addSQLiteStoreAndWait(fileName: "MyStore.sqlite")
}
catch {
// ...
}
``` ```
Simple transactions: Simple transactions:
@@ -131,7 +136,12 @@ This allows for a butter-smooth main thread, while still taking advantage of saf
## Setting up ## Setting up
The simplest way to initialize CoreStore is to add a default store to the default stack: The simplest way to initialize CoreStore is to add a default store to the default stack:
```swift ```swift
CoreStore.addSQLiteStoreAndWait() do {
try CoreStore.addSQLiteStoreAndWait()
}
catch {
// ...
}
``` ```
This one-liner does the following: This one-liner does the following:
- Triggers the lazy-initialization of `CoreStore.defaultStack` with a default `DataStack` - Triggers the lazy-initialization of `CoreStore.defaultStack` with a default `DataStack`
@@ -143,21 +153,24 @@ For most cases, this configuration is usable as it is. But for more hardcore set
```swift ```swift
let dataStack = DataStack(modelName: "MyModel") // loads from the "MyModel.xcdatamodeld" file let dataStack = DataStack(modelName: "MyModel") // loads from the "MyModel.xcdatamodeld" file
switch dataStack.addInMemoryStore(configuration: "Config1") { // creates an in-memory store with entities from the "Config1" configuration in the .xcdatamodeld file do {
case .Success(let persistentStore): // persistentStore is an NSPersistentStore instance // creates an in-memory store with entities from the "Config1" configuration in the .xcdatamodeld file
let persistentStore = try dataStack.addInMemoryStore(configuration: "Config1") // persistentStore is an NSPersistentStore instance
print("Successfully created an in-memory store: \(persistentStore)" print("Successfully created an in-memory store: \(persistentStore)"
case .Failure(let error): // error is an NSError instance }
catch let error as NSError {
print("Failed creating an in-memory store with error: \(error.description)" print("Failed creating an in-memory store with error: \(error.description)"
} }
switch dataStack.addSQLiteStoreAndWait( do {
fileURL: sqliteFileURL, // set the target file URL for the sqlite file try dataStack.addSQLiteStoreAndWait(
configuration: "Config2", // use entities from the "Config2" configuration in the .xcdatamodeld file fileURL: sqliteFileURL, // set the target file URL for the sqlite file
automigrating: true, // automatically run lightweight migrations or entity policy migrations when needed configuration: "Config2", // use entities from the "Config2" configuration in the .xcdatamodeld file
resetStoreOnMigrationFailure: true) { // delete and recreate the sqlite file when migration conflicts occur (useful when debugging) automigrating: true, // automatically run lightweight migrations or entity policy migrations when needed
case .Success(let persistentStore): // persistentStore is an NSPersistentStore instance resetStoreOnMigrationFailure: true)
print("Successfully created an sqlite store: \(persistentStore)" print("Successfully created an sqlite store: \(persistentStore)"
case .Failure(let error): // error is an NSError instance }
catch let error as NSError {
print("Failed creating an sqlite store with error: \(error.description)" print("Failed creating an sqlite store with error: \(error.description)"
} }
@@ -173,7 +186,11 @@ class MyViewController: UIViewController {
let dataStack = DataStack(modelName: "MyModel") let dataStack = DataStack(modelName: "MyModel")
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
self.dataStack.addSQLiteStoreAndWait() do {
try self.dataStack.addSQLiteStoreAndWait()
}
catch { // ...
}
} }
func methodToBeCalledLaterOn() { func methodToBeCalledLaterOn() {
let objects = self.dataStack.fetchAll(From(MyEntity)) let objects = self.dataStack.fetchAll(From(MyEntity))
@@ -186,7 +203,11 @@ The difference is when you set the stack as the `CoreStore.defaultStack`, you ca
class MyViewController: UIViewController { class MyViewController: UIViewController {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
CoreStore.addSQLiteStoreAndWait() do {
try CoreStore.addSQLiteStoreAndWait()
}
catch { // ...
}
} }
func methodToBeCalledLaterOn() { func methodToBeCalledLaterOn() {
let objects = CoreStore.fetchAll(From(MyEntity)) let objects = CoreStore.fetchAll(From(MyEntity))