From 5b0439835b720d72322892ee06448b667bbc75bd Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Mon, 21 Sep 2015 15:08:46 +0900 Subject: [PATCH] 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.