From 114b7ce60571f74ae937853b74752dca7e508c3c Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Sat, 19 Sep 2015 18:20:52 +0900 Subject: [PATCH 1/4] Tighter generics implementations. You can now pass any SequenceType's for methods that previously only accepts Array's. --- CoreStore.podspec | 2 +- .../BaseDataTransaction+Querying.swift | 24 +++----------- .../CoreStore+Querying.swift | 4 +-- .../DataStack+Querying.swift | 24 +++----------- .../BaseDataTransaction+Importing.swift | 32 ++++++------------- CoreStore/Info.plist | 2 +- CoreStore/Internal/Functions.swift | 8 ++--- .../AsynchronousDataTransaction.swift | 4 +-- .../BaseDataTransaction.swift | 9 ++---- .../SynchronousDataTransaction.swift | 4 +-- 10 files changed, 33 insertions(+), 80 deletions(-) diff --git a/CoreStore.podspec b/CoreStore.podspec index 66530b6..5395e1b 100644 --- a/CoreStore.podspec +++ b/CoreStore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "CoreStore" - s.version = "1.3.0" + s.version = "1.3.1" s.license = "MIT" s.summary = "Simple, elegant, and smart Core Data programming with Swift" s.homepage = "https://github.com/JohnEstropia/CoreStore" diff --git a/CoreStore/Fetching and Querying/BaseDataTransaction+Querying.swift b/CoreStore/Fetching and Querying/BaseDataTransaction+Querying.swift index ef01611..44496d9 100644 --- a/CoreStore/Fetching and Querying/BaseDataTransaction+Querying.swift +++ b/CoreStore/Fetching and Querying/BaseDataTransaction+Querying.swift @@ -78,17 +78,9 @@ public extension BaseDataTransaction { - returns: the `NSManagedObject` array for objects that exists in the transaction */ @warn_unused_result - public func fetchExisting(objects: [T]) -> [T] { + public func fetchExisting(objects: S) -> [T] { - var existingObjects = [T]() - for object in objects { - - if let existingObject = (try? self.context.existingObjectWithID(object.objectID)) as? T { - - existingObjects.append(existingObject) - } - } - return existingObjects + return objects.flatMap { (try? self.context.existingObjectWithID($0.objectID)) as? T } } /** @@ -98,17 +90,9 @@ public extension BaseDataTransaction { - returns: the `NSManagedObject` array for objects that exists in the transaction */ @warn_unused_result - public func fetchExisting(objectIDs: [NSManagedObjectID]) -> [T] { + public func fetchExisting(objectIDs: S) -> [T] { - var existingObjects = [T]() - for objectID in objectIDs { - - if let existingObject = (try? self.context.existingObjectWithID(objectID)) as? T { - - existingObjects.append(existingObject) - } - } - return existingObjects + return objectIDs.flatMap { (try? self.context.existingObjectWithID($0)) as? T } } /** diff --git a/CoreStore/Fetching and Querying/CoreStore+Querying.swift b/CoreStore/Fetching and Querying/CoreStore+Querying.swift index 3fb065c..05ffee5 100644 --- a/CoreStore/Fetching and Querying/CoreStore+Querying.swift +++ b/CoreStore/Fetching and Querying/CoreStore+Querying.swift @@ -62,7 +62,7 @@ public extension CoreStore { - returns: the `NSManagedObject` array for objects that exists in the `DataStack` */ @warn_unused_result - public static func fetchExisting(objects: [T]) -> [T] { + public static func fetchExisting(objects: S) -> [T] { return self.defaultStack.fetchExisting(objects) } @@ -74,7 +74,7 @@ public extension CoreStore { - returns: the `NSManagedObject` array for objects that exists in the `DataStack` */ @warn_unused_result - public static func fetchExisting(objectIDs: [NSManagedObjectID]) -> [T] { + public static func fetchExisting(objectIDs: S) -> [T] { return self.defaultStack.fetchExisting(objectIDs) } diff --git a/CoreStore/Fetching and Querying/DataStack+Querying.swift b/CoreStore/Fetching and Querying/DataStack+Querying.swift index 7c77842..5408b98 100644 --- a/CoreStore/Fetching and Querying/DataStack+Querying.swift +++ b/CoreStore/Fetching and Querying/DataStack+Querying.swift @@ -79,17 +79,9 @@ public extension DataStack { - returns: the `NSManagedObject` array for objects that exists in the `DataStack` */ @warn_unused_result - public func fetchExisting(objects: [T]) -> [T] { + public func fetchExisting(objects: S) -> [T] { - var existingObjects = [T]() - for object in objects { - - if let existingObject = (try? self.mainContext.existingObjectWithID(object.objectID)) as? T { - - existingObjects.append(existingObject) - } - } - return existingObjects + return objects.flatMap { (try? self.mainContext.existingObjectWithID($0.objectID)) as? T } } /** @@ -99,17 +91,9 @@ public extension DataStack { - returns: the `NSManagedObject` array for objects that exists in the `DataStack` */ @warn_unused_result - public func fetchExisting(objectIDs: [NSManagedObjectID]) -> [T] { + public func fetchExisting(objectIDs: S) -> [T] { - var existingObjects = [T]() - for objectID in objectIDs { - - if let existingObject = (try? self.mainContext.existingObjectWithID(objectID)) as? T { - - existingObjects.append(existingObject) - } - } - return existingObjects + return objectIDs.flatMap { (try? self.mainContext.existingObjectWithID($0)) as? T } } /** diff --git a/CoreStore/Importing Data/BaseDataTransaction+Importing.swift b/CoreStore/Importing Data/BaseDataTransaction+Importing.swift index a42ae0a..1d2f679 100644 --- a/CoreStore/Importing Data/BaseDataTransaction+Importing.swift +++ b/CoreStore/Importing Data/BaseDataTransaction+Importing.swift @@ -102,9 +102,9 @@ public extension BaseDataTransaction { - parameter sourceArray: the array of objects to import values from - parameter postProcess: a closure that exposes the array of created objects */ - public func importObjects( + public func importObjects( into: Into, - sourceArray: [T.ImportSource], + sourceArray: S, @noescape postProcess: (sorted: [T]) -> Void) throws { CoreStore.assert( @@ -185,10 +185,10 @@ public extension BaseDataTransaction { - parameter sourceArray: the array of objects to import values from - parameter preProcess: a closure that lets the caller tweak the internal `UniqueIDType`-to-`ImportSource` mapping to be used for importing. Callers can remove from/add to/update `mapping` and return the updated array from the closure. */ - public func importUniqueObjects( + public func importUniqueObjects( into: Into, - sourceArray: [T.ImportSource], - preProcess: ((mapping: [T.UniqueIDType: T.ImportSource]) throws -> [T.UniqueIDType: T.ImportSource])? = nil) throws { + sourceArray: S, + @noescape preProcess: (mapping: [T.UniqueIDType: T.ImportSource]) throws -> [T.UniqueIDType: T.ImportSource] = { $0 }) throws { CoreStore.assert( self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), @@ -211,13 +211,7 @@ public extension BaseDataTransaction { } } - if let preProcess = preProcess { - - try autoreleasepool { - - mapping = try preProcess(mapping: mapping) - } - } + mapping = try autoreleasepool { try preProcess(mapping: mapping) } for object in self.fetchAll(From(T), Where(T.uniqueIDKeyPath, isMemberOf: mapping.keys)) ?? [] { @@ -260,10 +254,10 @@ public extension BaseDataTransaction { - parameter preProcess: a closure that lets the caller tweak the internal `UniqueIDType`-to-`ImportSource` mapping to be used for importing. Callers can remove from/add to/update `mapping` and return the updated array from the closure. - parameter postProcess: a closure that exposes the array of created/updated objects */ - public func importUniqueObjects( + public func importUniqueObjects( into: Into, - sourceArray: [T.ImportSource], - preProcess: ((mapping: [T.UniqueIDType: T.ImportSource]) throws -> [T.UniqueIDType: T.ImportSource])? = nil, + sourceArray: S, + @noescape preProcess: (mapping: [T.UniqueIDType: T.ImportSource]) throws -> [T.UniqueIDType: T.ImportSource] = { $0 }, @noescape postProcess: (sorted: [T]) -> Void) throws { CoreStore.assert( @@ -289,13 +283,7 @@ public extension BaseDataTransaction { } } - if let preProcess = preProcess { - - try autoreleasepool { - - mapping = try preProcess(mapping: mapping) - } - } + mapping = try autoreleasepool { try preProcess(mapping: mapping) } var objects = Dictionary() for object in self.fetchAll(From(T), Where(T.uniqueIDKeyPath, isMemberOf: mapping.keys)) ?? [] { diff --git a/CoreStore/Info.plist b/CoreStore/Info.plist index 9f4a4aa..aa82cca 100644 --- a/CoreStore/Info.plist +++ b/CoreStore/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.3.0 + 1.3.1 CFBundleSignature ???? CFBundleVersion diff --git a/CoreStore/Internal/Functions.swift b/CoreStore/Internal/Functions.swift index 6fbba1d..e4d3b3a 100644 --- a/CoreStore/Internal/Functions.swift +++ b/CoreStore/Internal/Functions.swift @@ -25,9 +25,9 @@ import Foundation -internal func autoreleasepool(@noescape closure: () -> T?) -> T? { +internal func autoreleasepool(@noescape closure: () -> T) -> T { - var closureValue: T? + var closureValue: T! ObjectiveC.autoreleasepool { closureValue = closure() @@ -36,9 +36,9 @@ internal func autoreleasepool(@noescape closure: () -> T?) -> T? { return closureValue } -internal func autoreleasepool(@noescape closure: () throws -> T?) throws -> T? { +internal func autoreleasepool(@noescape closure: () throws -> T) throws -> T { - var closureValue: T? + var closureValue: T! var closureError: ErrorType? ObjectiveC.autoreleasepool { diff --git a/CoreStore/Saving and Processing/AsynchronousDataTransaction.swift b/CoreStore/Saving and Processing/AsynchronousDataTransaction.swift index f6c8591..8bcabed 100644 --- a/CoreStore/Saving and Processing/AsynchronousDataTransaction.swift +++ b/CoreStore/Saving and Processing/AsynchronousDataTransaction.swift @@ -171,7 +171,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction { "Attempted to delete an entities from an already committed \(typeName(self))." ) - super.delete([object1, object2] + objects) + super.delete(([object1, object2] + objects).flatMap { $0 }) } /** @@ -179,7 +179,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction { - parameter objects: the `NSManagedObject`s type to be deleted */ - public override func delete(objects: [NSManagedObject?]) { + public override func delete(objects: S) { CoreStore.assert( !self.isCommitted, diff --git a/CoreStore/Saving and Processing/BaseDataTransaction.swift b/CoreStore/Saving and Processing/BaseDataTransaction.swift index 38b1e0b..f5269d6 100644 --- a/CoreStore/Saving and Processing/BaseDataTransaction.swift +++ b/CoreStore/Saving and Processing/BaseDataTransaction.swift @@ -167,7 +167,7 @@ public /*abstract*/ class BaseDataTransaction { */ public func delete(object1: NSManagedObject?, _ object2: NSManagedObject?, _ objects: NSManagedObject?...) { - self.delete([object1, object2] + objects) + self.delete(([object1, object2] + objects).flatMap { $0 }) } /** @@ -175,7 +175,7 @@ public /*abstract*/ class BaseDataTransaction { - parameter objects: the `NSManagedObject`s to be deleted */ - public func delete(objects: [NSManagedObject?]) { + public func delete(objects: S) { CoreStore.assert( self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), @@ -183,10 +183,7 @@ public /*abstract*/ class BaseDataTransaction { ) let context = self.context - for case let object? in objects { - - context.fetchExisting(object)?.deleteFromContext() - } + objects.forEach { context.fetchExisting($0)?.deleteFromContext() } } // MARK: Saving changes diff --git a/CoreStore/Saving and Processing/SynchronousDataTransaction.swift b/CoreStore/Saving and Processing/SynchronousDataTransaction.swift index 0c62795..16023c0 100644 --- a/CoreStore/Saving and Processing/SynchronousDataTransaction.swift +++ b/CoreStore/Saving and Processing/SynchronousDataTransaction.swift @@ -161,7 +161,7 @@ public final class SynchronousDataTransaction: BaseDataTransaction { "Attempted to delete an entities from an already committed \(typeName(self))." ) - super.delete([object1, object2] + objects) + super.delete(([object1, object2] + objects).flatMap { $0 }) } /** @@ -169,7 +169,7 @@ public final class SynchronousDataTransaction: BaseDataTransaction { - parameter objects: the `NSManagedObject`s to be deleted */ - public override func delete(objects: [NSManagedObject?]) { + public override func delete(objects: S) { CoreStore.assert( !self.isCommitted, From 622c5aa652b817b595bd76da62ad67c12147ca1f Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Sat, 19 Sep 2015 19:45:01 +0900 Subject: [PATCH 2/4] Tighter generics implementations. You can now pass any SequenceType's for methods that previously only accepts Array's. --- .../BaseDataTransaction+Importing.swift | 137 ++---------------- 1 file changed, 15 insertions(+), 122 deletions(-) diff --git a/CoreStore/Importing Data/BaseDataTransaction+Importing.swift b/CoreStore/Importing Data/BaseDataTransaction+Importing.swift index 1d2f679..7493d12 100644 --- a/CoreStore/Importing Data/BaseDataTransaction+Importing.swift +++ b/CoreStore/Importing Data/BaseDataTransaction+Importing.swift @@ -67,70 +67,33 @@ public extension BaseDataTransaction { - parameter into: an `Into` clause specifying the entity type - parameter sourceArray: the array of objects to import values from - */ - public func importObjects( - into: Into, - sourceArray: [T.ImportSource]) throws { - - CoreStore.assert( - self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), - "Attempted to import an object of type \(typeName(into.entityClass)) outside the transaction's designated queue." - ) - - try autoreleasepool { - - for source in sourceArray { - - guard T.shouldInsertFromImportSource(source, inTransaction: self) else { - - continue - } - - try autoreleasepool { - - let object = self.create(into) - try object.didInsertFromImportSource(source, inTransaction: self) - } - } - } - } - - /** - Creates multiple `ImportableObject`s by importing from the specified array of import sources. - - - parameter into: an `Into` clause specifying the entity type - - parameter sourceArray: the array of objects to import values from - - parameter postProcess: a closure that exposes the array of created objects + - returns: the array of created `ImportableObject` instances */ public func importObjects( into: Into, - sourceArray: S, - @noescape postProcess: (sorted: [T]) -> Void) throws { + sourceArray: S) throws -> [T] { CoreStore.assert( self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), "Attempted to import an object of type \(typeName(into.entityClass)) outside the transaction's designated queue." ) - try autoreleasepool { + return try autoreleasepool { - var objects = [T]() - for source in sourceArray { + return try sourceArray.flatMap { (source) -> T? in guard T.shouldInsertFromImportSource(source, inTransaction: self) else { - continue + return nil } - try autoreleasepool { + return try autoreleasepool { let object = self.create(into) try object.didInsertFromImportSource(source, inTransaction: self) - - objects.append(object) + return object } } - postProcess(sorted: objects) } } @@ -184,102 +147,32 @@ public extension BaseDataTransaction { - parameter into: an `Into` clause specifying the entity type - parameter sourceArray: the array of objects to import values from - parameter preProcess: a closure that lets the caller tweak the internal `UniqueIDType`-to-`ImportSource` mapping to be used for importing. Callers can remove from/add to/update `mapping` and return the updated array from the closure. + - returns: the array of created/updated `ImportableUniqueObject` instances */ public func importUniqueObjects( into: Into, sourceArray: S, - @noescape preProcess: (mapping: [T.UniqueIDType: T.ImportSource]) throws -> [T.UniqueIDType: T.ImportSource] = { $0 }) throws { + @noescape preProcess: (mapping: [T.UniqueIDType: T.ImportSource]) throws -> [T.UniqueIDType: T.ImportSource] = { $0 }) throws -> [T] { CoreStore.assert( self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), "Attempted to import an object of type \(typeName(into.entityClass)) outside the transaction's designated queue." ) - try autoreleasepool { + return try autoreleasepool { var mapping = Dictionary() - for source in sourceArray { + let sortedIDs = try autoreleasepool { - try autoreleasepool { + return try sourceArray.flatMap { (source) -> T.UniqueIDType? in guard let uniqueIDValue = try T.uniqueIDFromImportSource(source, inTransaction: self) else { - return + return nil } mapping[uniqueIDValue] = source - } - } - - mapping = try autoreleasepool { try preProcess(mapping: mapping) } - - for object in self.fetchAll(From(T), Where(T.uniqueIDKeyPath, isMemberOf: mapping.keys)) ?? [] { - - try autoreleasepool { - - let uniqueIDValue = object.uniqueIDValue - - guard let source = mapping.removeValueForKey(uniqueIDValue) - where T.shouldUpdateFromImportSource(source, inTransaction: self) else { - - return - } - - try object.updateFromImportSource(source, inTransaction: self) - } - } - - for (uniqueIDValue, source) in mapping { - - try autoreleasepool { - - guard T.shouldInsertFromImportSource(source, inTransaction: self) else { - - return - } - - let object = self.create(into) - object.uniqueIDValue = uniqueIDValue - try object.didInsertFromImportSource(source, inTransaction: self) - } - } - } - } - - /** - Updates existing `ImportableUniqueObject`s or creates them by importing from the specified array of import sources. - - - parameter into: an `Into` clause specifying the entity type - - parameter sourceArray: the array of objects to import values from - - parameter preProcess: a closure that lets the caller tweak the internal `UniqueIDType`-to-`ImportSource` mapping to be used for importing. Callers can remove from/add to/update `mapping` and return the updated array from the closure. - - parameter postProcess: a closure that exposes the array of created/updated objects - */ - public func importUniqueObjects( - into: Into, - sourceArray: S, - @noescape preProcess: (mapping: [T.UniqueIDType: T.ImportSource]) throws -> [T.UniqueIDType: T.ImportSource] = { $0 }, - @noescape postProcess: (sorted: [T]) -> Void) throws { - - CoreStore.assert( - self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), - "Attempted to import an object of type \(typeName(into.entityClass)) outside the transaction's designated queue." - ) - - try autoreleasepool { - - var sortedIDs = Array() - var mapping = Dictionary() - for source in sourceArray { - - try autoreleasepool { - - guard let uniqueIDValue = try T.uniqueIDFromImportSource(source, inTransaction: self) else { - - return - } - - mapping[uniqueIDValue] = source - sortedIDs.append(uniqueIDValue) + return uniqueIDValue } } @@ -320,7 +213,7 @@ public extension BaseDataTransaction { } } - postProcess(sorted: sortedIDs.flatMap { objects[$0] }) + return sortedIDs.flatMap { objects[$0] } } } } From 5b0439835b720d72322892ee06448b667bbc75bd Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Mon, 21 Sep 2015 15:08:46 +0900 Subject: [PATCH 3/4] Deprecated DetachedDataTransaction in favor of UnsafeDataTransaction. beginDetached() methods are also deprecated; use beginUnsafe() instead. --- CoreStore.xcodeproj/project.pbxproj | 14 ++++---- .../CoreStore+Transaction.swift | 15 ++++++--- .../DataStack+Transaction.swift | 15 ++++++--- ...tion.swift => UnsafeDataTransaction.swift} | 33 ++++++++++++------- .../TransactionsDemoViewController.swift | 4 +-- CoreStoreTests/CoreStoreTests.swift | 16 ++++----- README.md | 12 +++---- 7 files changed, 67 insertions(+), 42 deletions(-) rename CoreStore/Saving and Processing/{DetachedDataTransaction.swift => UnsafeDataTransaction.swift} (57%) diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index c21fc3a..effbe37 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -13,7 +13,7 @@ 2F291E2719C6D3CF007AF63F /* CoreStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F291E2619C6D3CF007AF63F /* CoreStore.swift */; }; B504D0D61B02362500B2BBB1 /* CoreStore+Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B504D0D51B02362500B2BBB1 /* CoreStore+Setup.swift */; }; B51BE06A1B47FC4B0069F532 /* NSManagedObjectModel+Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51BE0691B47FC4B0069F532 /* NSManagedObjectModel+Setup.swift */; }; - B54A6A551BA15F2A007870FD /* FetchedResultsControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54A6A541BA15F2A007870FD /* FetchedResultsControllerDelegate.swift */; settings = {ASSET_TAGS = (); }; }; + B54A6A551BA15F2A007870FD /* FetchedResultsControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54A6A541BA15F2A007870FD /* FetchedResultsControllerDelegate.swift */; }; B56007111B3F6BD500A9A8F9 /* Into.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56007101B3F6BD500A9A8F9 /* Into.swift */; }; B56007141B3F6C2800A9A8F9 /* SectionBy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56007131B3F6C2800A9A8F9 /* SectionBy.swift */; }; B56007161B4018AB00A9A8F9 /* MigrationChain.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56007151B4018AB00A9A8F9 /* MigrationChain.swift */; }; @@ -38,7 +38,7 @@ B5E84EF41AFF846E0064E85B /* AsynchronousDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84EEA1AFF846E0064E85B /* AsynchronousDataTransaction.swift */; }; B5E84EF51AFF846E0064E85B /* BaseDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84EEB1AFF846E0064E85B /* BaseDataTransaction.swift */; }; B5E84EF61AFF846E0064E85B /* DataStack+Transaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84EEC1AFF846E0064E85B /* DataStack+Transaction.swift */; }; - B5E84EF71AFF846E0064E85B /* DetachedDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84EED1AFF846E0064E85B /* DetachedDataTransaction.swift */; }; + B5E84EF71AFF846E0064E85B /* UnsafeDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84EED1AFF846E0064E85B /* UnsafeDataTransaction.swift */; }; B5E84EF81AFF846E0064E85B /* CoreStore+Transaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84EEE1AFF846E0064E85B /* CoreStore+Transaction.swift */; }; B5E84EFB1AFF846E0064E85B /* SaveResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84EF21AFF846E0064E85B /* SaveResult.swift */; }; B5E84EFC1AFF846E0064E85B /* SynchronousDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84EF31AFF846E0064E85B /* SynchronousDataTransaction.swift */; }; @@ -67,8 +67,8 @@ B5E84F381AFF85470064E85B /* NSManagedObject+Transaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F341AFF85470064E85B /* NSManagedObject+Transaction.swift */; }; B5E84F391AFF85470064E85B /* NSManagedObjectContext+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F351AFF85470064E85B /* NSManagedObjectContext+Querying.swift */; }; B5E84F411AFF8CCD0064E85B /* ClauseTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F401AFF8CCD0064E85B /* ClauseTypes.swift */; }; - B5F1DA8D1B9AA97D007C5CBB /* ImportableObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F1DA8C1B9AA97D007C5CBB /* ImportableObject.swift */; settings = {ASSET_TAGS = (); }; }; - B5F1DA901B9AA991007C5CBB /* ImportableUniqueObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F1DA8F1B9AA991007C5CBB /* ImportableUniqueObject.swift */; settings = {ASSET_TAGS = (); }; }; + B5F1DA8D1B9AA97D007C5CBB /* ImportableObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F1DA8C1B9AA97D007C5CBB /* ImportableObject.swift */; }; + B5F1DA901B9AA991007C5CBB /* ImportableUniqueObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F1DA8F1B9AA991007C5CBB /* ImportableUniqueObject.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 */; }; B5FAD6AE1B518DCB00714891 /* CoreStore+Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FAD6AD1B518DCB00714891 /* CoreStore+Migration.swift */; }; @@ -144,7 +144,7 @@ B5E84EEA1AFF846E0064E85B /* AsynchronousDataTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsynchronousDataTransaction.swift; sourceTree = ""; }; B5E84EEB1AFF846E0064E85B /* BaseDataTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseDataTransaction.swift; sourceTree = ""; }; B5E84EEC1AFF846E0064E85B /* DataStack+Transaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DataStack+Transaction.swift"; sourceTree = ""; }; - B5E84EED1AFF846E0064E85B /* DetachedDataTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetachedDataTransaction.swift; sourceTree = ""; }; + B5E84EED1AFF846E0064E85B /* UnsafeDataTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnsafeDataTransaction.swift; sourceTree = ""; }; B5E84EEE1AFF846E0064E85B /* CoreStore+Transaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CoreStore+Transaction.swift"; sourceTree = ""; }; B5E84EF21AFF846E0064E85B /* SaveResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SaveResult.swift; sourceTree = ""; }; B5E84EF31AFF846E0064E85B /* SynchronousDataTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronousDataTransaction.swift; sourceTree = ""; }; @@ -357,7 +357,7 @@ B5E84EEB1AFF846E0064E85B /* BaseDataTransaction.swift */, B5E84EEA1AFF846E0064E85B /* AsynchronousDataTransaction.swift */, B5E84EF31AFF846E0064E85B /* SynchronousDataTransaction.swift */, - B5E84EED1AFF846E0064E85B /* DetachedDataTransaction.swift */, + B5E84EED1AFF846E0064E85B /* UnsafeDataTransaction.swift */, B5E84EEC1AFF846E0064E85B /* DataStack+Transaction.swift */, B5E84EEE1AFF846E0064E85B /* CoreStore+Transaction.swift */, B5E84EF21AFF846E0064E85B /* SaveResult.swift */, @@ -606,7 +606,7 @@ B5E84EDF1AFF84500064E85B /* DataStack.swift in Sources */, B5E834BB1B7691F3001D3D50 /* Functions.swift in Sources */, B5E84F231AFF84860064E85B /* ListMonitor.swift in Sources */, - B5E84EF71AFF846E0064E85B /* DetachedDataTransaction.swift in Sources */, + B5E84EF71AFF846E0064E85B /* UnsafeDataTransaction.swift in Sources */, B56964D41B22FFAD0075EE4A /* DataStack+Migration.swift in Sources */, B5E84EF51AFF846E0064E85B /* BaseDataTransaction.swift in Sources */, B5E84EFB1AFF846E0064E85B /* SaveResult.swift in Sources */, diff --git a/CoreStore/Saving and Processing/CoreStore+Transaction.swift b/CoreStore/Saving and Processing/CoreStore+Transaction.swift index 4d98d39..00fbd38 100644 --- a/CoreStore/Saving and Processing/CoreStore+Transaction.swift +++ b/CoreStore/Saving and Processing/CoreStore+Transaction.swift @@ -54,13 +54,20 @@ public extension CoreStore { } /** - Using the `defaultStack`, begins a non-contiguous transaction where `NSManagedObject` creates, updates, and deletes can be made. This is useful for making temporary changes, such as partially filled forms. A detached transaction object should typically be only used from the main queue. + Using the `defaultStack`, begins a non-contiguous transaction where `NSManagedObject` creates, updates, and deletes can be made. This is useful for making temporary changes, such as partially filled forms. An unsafe transaction object should typically be only used from the main queue. - - returns: a `DetachedDataTransaction` instance where creates, updates, and deletes can be made. + - returns: a `UnsafeDataTransaction` instance where creates, updates, and deletes can be made. */ @warn_unused_result - public static func beginDetached() -> DetachedDataTransaction { + public static func beginUnsafe() -> UnsafeDataTransaction { - return self.defaultStack.beginDetached() + return self.defaultStack.beginUnsafe() + } + + @available(*, deprecated=1.3.1, renamed="beginUnsafe") + @warn_unused_result + public static func beginDetached() -> UnsafeDataTransaction { + + return self.beginUnsafe() } } diff --git a/CoreStore/Saving and Processing/DataStack+Transaction.swift b/CoreStore/Saving and Processing/DataStack+Transaction.swift index 4a0f6c9..aa00a64 100644 --- a/CoreStore/Saving and Processing/DataStack+Transaction.swift +++ b/CoreStore/Saving and Processing/DataStack+Transaction.swift @@ -64,17 +64,24 @@ public extension DataStack { /** Begins a non-contiguous transaction where `NSManagedObject` creates, updates, and deletes can be made. This is useful for making temporary changes, such as partially filled forms. - - returns: a `DetachedDataTransaction` instance where creates, updates, and deletes can be made. + - returns: a `UnsafeDataTransaction` instance where creates, updates, and deletes can be made. */ @warn_unused_result - public func beginDetached() -> DetachedDataTransaction { + public func beginUnsafe() -> UnsafeDataTransaction { - return DetachedDataTransaction( + return UnsafeDataTransaction( mainContext: self.rootSavingContext, queue: .createSerial( - "com.coreStore.dataStack.detachedTransactionQueue", + "com.coreStore.dataStack.unsafeTransactionQueue", targetQueue: .UserInitiated ) ) } + + @available(*, deprecated=1.3.1, renamed="beginUnsafe") + @warn_unused_result + public func beginDetached() -> UnsafeDataTransaction { + + return self.beginUnsafe() + } } diff --git a/CoreStore/Saving and Processing/DetachedDataTransaction.swift b/CoreStore/Saving and Processing/UnsafeDataTransaction.swift similarity index 57% rename from CoreStore/Saving and Processing/DetachedDataTransaction.swift rename to CoreStore/Saving and Processing/UnsafeDataTransaction.swift index 97f150d..09e8594 100644 --- a/CoreStore/Saving and Processing/DetachedDataTransaction.swift +++ b/CoreStore/Saving and Processing/UnsafeDataTransaction.swift @@ -1,5 +1,5 @@ // -// DetachedDataTransaction.swift +// UnsafeDataTransaction.swift // CoreStore // // Copyright (c) 2015 John Rommel Estropia @@ -27,17 +27,21 @@ import Foundation import GCDKit -// MARK: - DetachedDataTransaction +@available(*, deprecated=1.3.1, renamed="UnsafeDataTransaction") +public typealias DetachedDataTransaction = UnsafeDataTransaction + + +// MARK: - UnsafeDataTransaction /** -The `DetachedDataTransaction` provides an interface for non-contiguous `NSManagedObject` creates, updates, and deletes. This is useful for making temporary changes, such as partially filled forms. A detached transaction object should typically be only used from the main queue. +The `UnsafeDataTransaction` provides an interface for non-contiguous `NSManagedObject` creates, updates, and deletes. This is useful for making temporary changes, such as partially filled forms. An unsafe transaction object should typically be only used from the main queue. */ -public final class DetachedDataTransaction: BaseDataTransaction { +public final class UnsafeDataTransaction: BaseDataTransaction { // MARK: Public /** - Saves the transaction changes asynchronously. For a `DetachedDataTransaction`, multiple commits are allowed, although it is the developer's responsibility to ensure a reasonable leeway to prevent blocking the main thread. + Saves the transaction changes asynchronously. For a `UnsafeDataTransaction`, multiple commits are allowed, although it is the developer's responsibility to ensure a reasonable leeway to prevent blocking the main thread. - parameter completion: the block executed after the save completes. Success or failure is reported by the `SaveResult` argument of the block. */ @@ -53,29 +57,36 @@ public final class DetachedDataTransaction: BaseDataTransaction { /** Begins a child transaction where `NSManagedObject` creates, updates, and deletes can be made. This is useful for making temporary changes, such as partially filled forms. - - returns: a `DetachedDataTransaction` instance where creates, updates, and deletes can be made. + - returns: a `UnsafeDataTransaction` instance where creates, updates, and deletes can be made. */ @warn_unused_result - public func beginDetached() -> DetachedDataTransaction { + public func beginUnsafe() -> UnsafeDataTransaction { - return DetachedDataTransaction( + return UnsafeDataTransaction( mainContext: self.context, queue: self.transactionQueue ) } /** - Returns the `NSManagedObjectContext` for this detached transaction. Use only for cases where external frameworks need an `NSManagedObjectContext` instance to work with. + Returns the `NSManagedObjectContext` for this unsafe transaction. Use only for cases where external frameworks need an `NSManagedObjectContext` instance to work with. Note that it is the developer's responsibility to ensure the following: - - that the `DetachedDataTransaction` that owns this context should be strongly referenced and prevented from being deallocated during the context's lifetime - - that all saves will be done either through the `DetachedDataTransaction`'s `commit(...)` method, or by calling `save()` manually on the context, its parent, and all other ancestor contexts if there are any. + - that the `UnsafeDataTransaction` that owns this context should be strongly referenced and prevented from being deallocated during the context's lifetime + - that all saves will be done either through the `UnsafeDataTransaction`'s `commit(...)` method, or by calling `save()` manually on the context, its parent, and all other ancestor contexts if there are any. */ public var internalContext: NSManagedObjectContext { return self.context } + @available(*, deprecated=1.3.1, renamed="beginUnsafe") + @warn_unused_result + public func beginDetached() -> UnsafeDataTransaction { + + return self.beginUnsafe() + } + // MARK: Internal diff --git a/CoreStoreDemo/CoreStoreDemo/Transactions Demo/TransactionsDemoViewController.swift b/CoreStoreDemo/CoreStoreDemo/Transactions Demo/TransactionsDemoViewController.swift index a2af274..9f1962d 100644 --- a/CoreStoreDemo/CoreStoreDemo/Transactions Demo/TransactionsDemoViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/Transactions Demo/TransactionsDemoViewController.swift @@ -78,7 +78,7 @@ class TransactionsDemoViewController: UIViewController, MKMapViewDelegate, Objec let alert = UIAlertController( title: "Transactions Demo", - message: "This demo shows how to use the 3 types of transactions to save updates: synchronous, asynchronous, and detached.\n\nTap and hold on the map to change the pin location.", + message: "This demo shows how to use the 3 types of transactions to save updates: synchronous, asynchronous, and unsafe.\n\nTap and hold on the map to change the pin location.", preferredStyle: .Alert ) alert.addAction(UIAlertAction(title: "OK", style: .Cancel, handler: nil)) @@ -184,7 +184,7 @@ class TransactionsDemoViewController: UIViewController, MKMapViewDelegate, Objec func geocodePlace(place: Place) { - let transaction = CoreStore.beginDetached() + let transaction = CoreStore.beginUnsafe() self.geocoder?.cancelGeocode() diff --git a/CoreStoreTests/CoreStoreTests.swift b/CoreStoreTests/CoreStoreTests.swift index 81bc2a2..be80482 100644 --- a/CoreStoreTests/CoreStoreTests.swift +++ b/CoreStoreTests/CoreStoreTests.swift @@ -103,7 +103,7 @@ class CoreStoreTests: XCTestCase { XCTFail(error.description) } - let detachedTransaction = CoreStore.beginDetached() + let unsafeTransaction = CoreStore.beginUnsafe() let createExpectation = self.expectationWithDescription("Entity creation") CoreStore.beginAsynchronous { (transaction) -> Void in @@ -182,7 +182,7 @@ class CoreStoreTests: XCTestCase { ) XCTAssertNil(objs4test, "objs4test == nil") - let objs5test = detachedTransaction.fetchCount(From(TestEntity2)) + let objs5test = unsafeTransaction.fetchCount(From(TestEntity2)) XCTAssertTrue(objs5test == 3, "objs5test == 3") XCTAssertTrue(NSThread.isMainThread(), "NSThread.isMainThread()") @@ -289,15 +289,15 @@ class CoreStoreTests: XCTestCase { XCTAssertNotNil(objs2, "objs2 != nil") XCTAssertTrue(objs2?.count == 1, "objs2?.count == 1") - let detachedExpectation = self.expectationWithDescription("Query creation") + let unsafeExpectation = self.expectationWithDescription("Query creation") - let obj5 = detachedTransaction.create(Into("Config1")) + let obj5 = unsafeTransaction.create(Into("Config1")) obj5.testEntityID = 5 obj5.testString = "hihihi" obj5.testNumber = 70 obj5.testDate = NSDate() - detachedTransaction.commit { (result) -> Void in + unsafeTransaction.commit { (result) -> Void in XCTAssertTrue(NSThread.isMainThread(), "NSThread.isMainThread()") switch result { @@ -322,13 +322,13 @@ class CoreStoreTests: XCTestCase { ) XCTAssertTrue(count == 1, "count == 1 (actual: \(count))") - let obj6 = detachedTransaction.create(Into()) + let obj6 = unsafeTransaction.create(Into()) obj6.testEntityID = 6 obj6.testString = "huehuehue" obj6.testNumber = 130 obj6.testDate = NSDate() - detachedTransaction.commit { (result) -> Void in + unsafeTransaction.commit { (result) -> Void in XCTAssertTrue(NSThread.isMainThread(), "NSThread.isMainThread()") switch result { @@ -358,7 +358,7 @@ class CoreStoreTests: XCTestCase { ) XCTAssertTrue(count2 == 0, "count == 0 (actual: \(count2))") - detachedExpectation.fulfill() + unsafeExpectation.fulfill() case .Failure(let error): XCTFail(error.description) diff --git a/README.md b/README.md index 983fbbc..f6e152d 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Unleashing the real power of Core Data with the elegance and safety of Swift - [Transaction types](#transaction-types) - [Asynchronous transactions](#asynchronous-transactions) - [Synchronous transactions](#synchronous-transactions) - - [Detached transactions](#detached-transactions) + - [Unsafe transactions](#unsafe-transactions) - [Creating objects](#creating-objects) - [Updating objects](#updating-objects) - [Deleting objects](#deleting-objects) @@ -146,7 +146,7 @@ If you are already familiar with the inner workings of CoreData, here is a mappi | --- | --- | | `NSManagedObjectModel` / `NSPersistentStoreCoordinator`
(.xcdatamodeld file) | `DataStack` | | `NSPersistentStore`
("Configuration"s in the .xcdatamodeld file) | `DataStack` configuration
(multiple sqlite / in-memory stores per stack) | -| `NSManagedObjectContext` | `BaseDataTransaction` subclasses
(`SynchronousDataTransaction`, `AsynchronousDataTransaction`, `DetachedDataTransaction`) | +| `NSManagedObjectContext` | `BaseDataTransaction` subclasses
(`SynchronousDataTransaction`, `AsynchronousDataTransaction`, `UnsafeDataTransaction`) | Popular libraries [RestKit](https://github.com/RestKit/RestKit) and [MagicalRecord](https://github.com/magicalpanda/MagicalRecord) set up their `NSManagedObjectContext`s this way: @@ -376,7 +376,7 @@ CoreStore.beginAsynchronous { (transaction) -> Void in ``` The `commit()` method saves the changes to the persistent store. If `commit()` is not called when the transaction block completes, all changes within the transaction is discarded. -The examples above use `beginAsynchronous(...)`, but there are actually 3 types of transactions at your disposal: *asynchronous*, *synchronous*, and *detached*. +The examples above use `beginAsynchronous(...)`, but there are actually 3 types of transactions at your disposal: *asynchronous*, *synchronous*, and *unsafe*. ### Transaction types @@ -402,10 +402,10 @@ CoreStore.beginSynchronous { (transaction) -> Void in Since `beginSynchronous(...)` technically blocks two queues (the caller's queue and the transaction's background queue), it is considered less safe as it's more prone to deadlock. Take special care that the closure does not block on any other external queues. -#### Detached transactions +#### Unsafe transactions are special in that they do not enclose updates within a closure: ```swift -let transaction = CoreStore.beginDetached() +let transaction = CoreStore.beginUnsafe() // make changes downloadJSONWithCompletion({ (json) -> Void in @@ -420,7 +420,7 @@ downloadAnotherJSONWithCompletion({ (json) -> Void in ``` This allows for non-contiguous updates. Do note that this flexibility comes with a price: you are now responsible for managing concurrency for the transaction. As uncle Ben said, "with great power comes great race conditions." -As the above example also shows, only detached transactions are allowed to call `commit()` multiple times; doing so with synchronous and asynchronous transactions will trigger an assert. +As the above example also shows, only unsafe transactions are allowed to call `commit()` multiple times; doing so with synchronous and asynchronous transactions will trigger an assert. You've seen how to create transactions, but we have yet to see how to make *creates*, *updates*, and *deletes*. The 3 types of transactions above are all subclasses of `BaseDataTransaction`, which implements the methods shown below. From b9ec66f42527554e60b39e37787ecea0073924a1 Mon Sep 17 00:00:00 2001 From: John Estropia Date: Mon, 28 Sep 2015 20:44:19 +0900 Subject: [PATCH 4/4] fix entityNameMapping bug --- CoreStore/Internal/NSManagedObjectModel+Setup.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CoreStore/Internal/NSManagedObjectModel+Setup.swift b/CoreStore/Internal/NSManagedObjectModel+Setup.swift index 68da04a..ace2548 100644 --- a/CoreStore/Internal/NSManagedObjectModel+Setup.swift +++ b/CoreStore/Internal/NSManagedObjectModel+Setup.swift @@ -154,7 +154,7 @@ internal extension NSManagedObjectModel { return self.entityNameMapping.reduce([:]) { (var mapping, pair) in - mapping[pair.0] = (NSClassFromString(pair.1)! as! NSManagedObject.Type) + mapping[pair.1] = (NSClassFromString(pair.0)! as! NSManagedObject.Type) return mapping } }