mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-03-10 15:16:11 +01:00
new auto-commit transaction methods
This commit is contained in:
@@ -398,10 +398,6 @@
|
||||
B59FA0B11CCBACA7007C9BCA /* ICloudStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59FA0AD1CCBAC95007C9BCA /* ICloudStore.swift */; };
|
||||
B59FA0B21CCBACA8007C9BCA /* ICloudStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59FA0AD1CCBAC95007C9BCA /* ICloudStore.swift */; };
|
||||
B5A261211B64BFDB006EB6D3 /* MigrationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A261201B64BFDB006EB6D3 /* MigrationType.swift */; };
|
||||
B5A27F861E857C5300203C3E /* TransactionResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A27F851E857C5300203C3E /* TransactionResult.swift */; };
|
||||
B5A27F871E857C5300203C3E /* TransactionResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A27F851E857C5300203C3E /* TransactionResult.swift */; };
|
||||
B5A27F881E857C5300203C3E /* TransactionResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A27F851E857C5300203C3E /* TransactionResult.swift */; };
|
||||
B5A27F891E857C5300203C3E /* TransactionResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A27F851E857C5300203C3E /* TransactionResult.swift */; };
|
||||
B5A5F2661CAEC50F004AB9AF /* CSSelect.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A5F2651CAEC50F004AB9AF /* CSSelect.swift */; };
|
||||
B5A5F2681CAEC50F004AB9AF /* CSSelect.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A5F2651CAEC50F004AB9AF /* CSSelect.swift */; };
|
||||
B5A5F2691CAEC50F004AB9AF /* CSSelect.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A5F2651CAEC50F004AB9AF /* CSSelect.swift */; };
|
||||
@@ -689,7 +685,6 @@
|
||||
B59AFF401C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSPersistentStoreCoordinator+Setup.swift"; sourceTree = "<group>"; };
|
||||
B59FA0AD1CCBAC95007C9BCA /* ICloudStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ICloudStore.swift; sourceTree = "<group>"; };
|
||||
B5A261201B64BFDB006EB6D3 /* MigrationType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MigrationType.swift; sourceTree = "<group>"; };
|
||||
B5A27F851E857C5300203C3E /* TransactionResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionResult.swift; sourceTree = "<group>"; };
|
||||
B5A5F2651CAEC50F004AB9AF /* CSSelect.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSSelect.swift; sourceTree = "<group>"; };
|
||||
B5AD60CD1C90141E00F2B2E8 /* Package.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = SOURCE_ROOT; };
|
||||
B5AEFAB41C9962AE00AD137F /* CoreStoreBridge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreStoreBridge.swift; sourceTree = "<group>"; };
|
||||
@@ -1165,7 +1160,6 @@
|
||||
B5E84EEE1AFF846E0064E85B /* CoreStore+Transaction.swift */,
|
||||
B50392F81C478FF3009900CA /* NSManagedObject+Transaction.swift */,
|
||||
B5E84EF21AFF846E0064E85B /* SaveResult.swift */,
|
||||
B5A27F851E857C5300203C3E /* TransactionResult.swift */,
|
||||
);
|
||||
path = Transactions;
|
||||
sourceTree = "<group>";
|
||||
@@ -1609,7 +1603,6 @@
|
||||
B549F6731E56A92800FBAB2D /* CoreDataNativeType.swift in Sources */,
|
||||
B509C7F41E54511B0061C547 /* ImportableAttributeType.swift in Sources */,
|
||||
B5E84F0E1AFF847B0064E85B /* Tweak.swift in Sources */,
|
||||
B5A27F861E857C5300203C3E /* TransactionResult.swift in Sources */,
|
||||
B5E1B5931CAA0C15007FD580 /* CSObjectMonitor.swift in Sources */,
|
||||
B5ECDC291CA81CC700C7F112 /* CSDataStack+Transaction.swift in Sources */,
|
||||
B5E84F121AFF847B0064E85B /* OrderBy.swift in Sources */,
|
||||
@@ -1766,7 +1759,6 @@
|
||||
B549F6741E56A92800FBAB2D /* CoreDataNativeType.swift in Sources */,
|
||||
B509C7F51E54511B0061C547 /* ImportableAttributeType.swift in Sources */,
|
||||
82BA18B31C4BBD3900A0916E /* ImportableUniqueObject.swift in Sources */,
|
||||
B5A27F871E857C5300203C3E /* TransactionResult.swift in Sources */,
|
||||
B5E1B5951CAA0C15007FD580 /* CSObjectMonitor.swift in Sources */,
|
||||
B5ECDC2B1CA81CC700C7F112 /* CSDataStack+Transaction.swift in Sources */,
|
||||
82BA18A11C4BBD1D00A0916E /* CoreStore.swift in Sources */,
|
||||
@@ -1923,7 +1915,6 @@
|
||||
B549F6761E56A92800FBAB2D /* CoreDataNativeType.swift in Sources */,
|
||||
B509C7F71E54511B0061C547 /* ImportableAttributeType.swift in Sources */,
|
||||
B5220E1F1D130810009BC71E /* CSListObserver.swift in Sources */,
|
||||
B5A27F891E857C5300203C3E /* TransactionResult.swift in Sources */,
|
||||
B52DD1941BE1F92500949AFE /* CoreStore.swift in Sources */,
|
||||
B52DD1A61BE1F92F00949AFE /* BaseDataTransaction+Importing.swift in Sources */,
|
||||
B5220E1D1D13080A009BC71E /* CSDataStack+Observing.swift in Sources */,
|
||||
@@ -2080,7 +2071,6 @@
|
||||
B549F6751E56A92800FBAB2D /* CoreDataNativeType.swift in Sources */,
|
||||
B509C7F61E54511B0061C547 /* ImportableAttributeType.swift in Sources */,
|
||||
B5E1B5961CAA0C15007FD580 /* CSObjectMonitor.swift in Sources */,
|
||||
B5A27F881E857C5300203C3E /* TransactionResult.swift in Sources */,
|
||||
B5ECDC2C1CA81CC700C7F112 /* CSDataStack+Transaction.swift in Sources */,
|
||||
B56321911BD65216006C9394 /* BaseDataTransaction+Importing.swift in Sources */,
|
||||
B546F95A1C99B17400D5AC55 /* CSCoreStore+Setup.swift in Sources */,
|
||||
|
||||
@@ -30,47 +30,48 @@ class BaseTestDataTestCase: BaseTestCase {
|
||||
@nonobjc
|
||||
func prepareTestDataForStack(_ stack: DataStack, configurations: [String?] = [nil]) {
|
||||
|
||||
stack.beginSynchronous { (transaction) in
|
||||
|
||||
for (configurationIndex, configuration) in configurations.enumerated() {
|
||||
try! stack.perform(
|
||||
synchronous: { (transaction) in
|
||||
|
||||
let configurationOrdinal = configurationIndex + 1
|
||||
if configuration == nil || configuration == "Config1" {
|
||||
for (configurationIndex, configuration) in configurations.enumerated() {
|
||||
|
||||
for idIndex in 1 ... 5 {
|
||||
let configurationOrdinal = configurationIndex + 1
|
||||
if configuration == nil || configuration == "Config1" {
|
||||
|
||||
let object = transaction.create(Into<TestEntity1>(configuration))
|
||||
object.testEntityID = NSNumber(value: (configurationOrdinal * 100) + idIndex)
|
||||
|
||||
object.testNumber = NSNumber(value: idIndex)
|
||||
object.testDate = self.dateFormatter.date(from: "2000-\(configurationOrdinal)-\(idIndex)T00:00:00Z")
|
||||
object.testBoolean = NSNumber(value: (idIndex % 2) == 1)
|
||||
object.testDecimal = NSDecimalNumber(string: "\(idIndex)")
|
||||
|
||||
let string = "\(configuration ?? "nil"):TestEntity1:\(idIndex)"
|
||||
object.testString = string
|
||||
object.testData = (string as NSString).data(using: String.Encoding.utf8.rawValue)
|
||||
for idIndex in 1 ... 5 {
|
||||
|
||||
let object = transaction.create(Into<TestEntity1>(configuration))
|
||||
object.testEntityID = NSNumber(value: (configurationOrdinal * 100) + idIndex)
|
||||
|
||||
object.testNumber = NSNumber(value: idIndex)
|
||||
object.testDate = self.dateFormatter.date(from: "2000-\(configurationOrdinal)-\(idIndex)T00:00:00Z")
|
||||
object.testBoolean = NSNumber(value: (idIndex % 2) == 1)
|
||||
object.testDecimal = NSDecimalNumber(string: "\(idIndex)")
|
||||
|
||||
let string = "\(configuration ?? "nil"):TestEntity1:\(idIndex)"
|
||||
object.testString = string
|
||||
object.testData = (string as NSString).data(using: String.Encoding.utf8.rawValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
if configuration == nil || configuration == "Config2" {
|
||||
|
||||
for idIndex in 1 ... 5 {
|
||||
if configuration == nil || configuration == "Config2" {
|
||||
|
||||
let object = transaction.create(Into<TestEntity2>(configuration))
|
||||
object.testEntityID = NSNumber(value: (configurationOrdinal * 200) + idIndex)
|
||||
|
||||
object.testNumber = NSNumber(value: idIndex)
|
||||
object.testDate = self.dateFormatter.date(from: "2000-\(configurationOrdinal)-\(idIndex)T00:00:00Z")
|
||||
object.testBoolean = NSNumber(value: (idIndex % 2) == 1)
|
||||
object.testDecimal = NSDecimalNumber(string: "\(idIndex)")
|
||||
|
||||
let string = "\(configuration ?? "nil"):TestEntity2:\(idIndex)"
|
||||
object.testString = string
|
||||
object.testData = (string as NSString).data(using: String.Encoding.utf8.rawValue)
|
||||
for idIndex in 1 ... 5 {
|
||||
|
||||
let object = transaction.create(Into<TestEntity2>(configuration))
|
||||
object.testEntityID = NSNumber(value: (configurationOrdinal * 200) + idIndex)
|
||||
|
||||
object.testNumber = NSNumber(value: idIndex)
|
||||
object.testDate = self.dateFormatter.date(from: "2000-\(configurationOrdinal)-\(idIndex)T00:00:00Z")
|
||||
object.testBoolean = NSNumber(value: (idIndex % 2) == 1)
|
||||
object.testDecimal = NSDecimalNumber(string: "\(idIndex)")
|
||||
|
||||
let string = "\(configuration ?? "nil"):TestEntity2:\(idIndex)"
|
||||
object.testString = string
|
||||
object.testData = (string as NSString).data(using: String.Encoding.utf8.rawValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = transaction.commitAndWait()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,6 +212,9 @@
|
||||
CSUnsafeDataTransaction *transaction = [CSCoreStore beginUnsafe];
|
||||
XCTAssertNotNil(transaction);
|
||||
XCTAssert([transaction isKindOfClass:[CSUnsafeDataTransaction class]]);
|
||||
NSError *error;
|
||||
XCTAssertTrue([transaction commitAndWaitWithError:&error]);
|
||||
XCTAssertNil(error);
|
||||
}
|
||||
{
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"sync"];
|
||||
@@ -219,6 +222,9 @@
|
||||
|
||||
XCTAssertNotNil(transaction);
|
||||
XCTAssert([transaction isKindOfClass:[CSSynchronousDataTransaction class]]);
|
||||
NSError *error;
|
||||
XCTAssertTrue([transaction commitAndWaitWithError:&error]);
|
||||
XCTAssertNil(error);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
}
|
||||
@@ -228,7 +234,15 @@
|
||||
|
||||
XCTAssertNotNil(transaction);
|
||||
XCTAssert([transaction isKindOfClass:[CSAsynchronousDataTransaction class]]);
|
||||
[expectation fulfill];
|
||||
[transaction
|
||||
commitWithSuccess:^{
|
||||
|
||||
[expectation fulfill];
|
||||
}
|
||||
failure:^(CSError *error){
|
||||
|
||||
XCTFail();
|
||||
}];
|
||||
}];
|
||||
}
|
||||
[self waitForExpectationsWithTimeout:10 handler:nil];
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -149,29 +149,29 @@ class ListObserverTests: BaseTestDataTestCase {
|
||||
}
|
||||
)
|
||||
let saveExpectation = self.expectation(description: "save")
|
||||
stack.beginAsynchronous { (transaction) in
|
||||
|
||||
let object = transaction.create(Into<TestEntity1>())
|
||||
object.testBoolean = NSNumber(value: true)
|
||||
object.testNumber = NSNumber(value: 1)
|
||||
object.testDecimal = NSDecimalNumber(string: "1")
|
||||
object.testString = "nil:TestEntity1:1"
|
||||
object.testData = ("nil:TestEntity1:1" as NSString).data(using: String.Encoding.utf8.rawValue)!
|
||||
object.testDate = self.dateFormatter.date(from: "2000-01-01T00:00:00Z")!
|
||||
|
||||
transaction.commit { (result) in
|
||||
stack.perform(
|
||||
asynchronous: { (transaction) -> Bool in
|
||||
|
||||
switch result {
|
||||
|
||||
case .success(let hasChanges):
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
|
||||
case .failure:
|
||||
XCTFail()
|
||||
}
|
||||
let object = transaction.create(Into<TestEntity1>())
|
||||
object.testBoolean = NSNumber(value: true)
|
||||
object.testNumber = NSNumber(value: 1)
|
||||
object.testDecimal = NSDecimalNumber(string: "1")
|
||||
object.testString = "nil:TestEntity1:1"
|
||||
object.testData = ("nil:TestEntity1:1" as NSString).data(using: String.Encoding.utf8.rawValue)!
|
||||
object.testDate = self.dateFormatter.date(from: "2000-01-01T00:00:00Z")!
|
||||
|
||||
return transaction.hasChanges
|
||||
},
|
||||
success: { (hasChanges) in
|
||||
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
},
|
||||
failure: { _ in
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
}
|
||||
)
|
||||
self.waitAndCheckExpectations()
|
||||
}
|
||||
}
|
||||
@@ -283,49 +283,49 @@ class ListObserverTests: BaseTestDataTestCase {
|
||||
}
|
||||
)
|
||||
let saveExpectation = self.expectation(description: "save")
|
||||
stack.beginAsynchronous { (transaction) in
|
||||
|
||||
if let object = transaction.fetchOne(
|
||||
From<TestEntity1>(),
|
||||
Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) {
|
||||
stack.perform(
|
||||
asynchronous: { (transaction) -> Bool in
|
||||
|
||||
object.testNumber = NSNumber(value: 11)
|
||||
object.testDecimal = NSDecimalNumber(string: "11")
|
||||
object.testString = "nil:TestEntity1:11"
|
||||
object.testData = ("nil:TestEntity1:11" as NSString).data(using: String.Encoding.utf8.rawValue)!
|
||||
object.testDate = self.dateFormatter.date(from: "2000-01-11T00:00:00Z")!
|
||||
}
|
||||
else {
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
if let object = transaction.fetchOne(
|
||||
From<TestEntity1>(),
|
||||
Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 102)) {
|
||||
|
||||
object.testNumber = NSNumber(value: 22)
|
||||
object.testDecimal = NSDecimalNumber(string: "22")
|
||||
object.testString = "nil:TestEntity1:22"
|
||||
object.testData = ("nil:TestEntity1:22" as NSString).data(using: String.Encoding.utf8.rawValue)!
|
||||
object.testDate = self.dateFormatter.date(from: "2000-01-22T00:00:00Z")!
|
||||
}
|
||||
else {
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
transaction.commit { (result) in
|
||||
|
||||
switch result {
|
||||
if let object = transaction.fetchOne(
|
||||
From<TestEntity1>(),
|
||||
Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) {
|
||||
|
||||
case .success(let hasChanges):
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
object.testNumber = NSNumber(value: 11)
|
||||
object.testDecimal = NSDecimalNumber(string: "11")
|
||||
object.testString = "nil:TestEntity1:11"
|
||||
object.testData = ("nil:TestEntity1:11" as NSString).data(using: String.Encoding.utf8.rawValue)!
|
||||
object.testDate = self.dateFormatter.date(from: "2000-01-11T00:00:00Z")!
|
||||
}
|
||||
else {
|
||||
|
||||
case .failure:
|
||||
XCTFail()
|
||||
}
|
||||
if let object = transaction.fetchOne(
|
||||
From<TestEntity1>(),
|
||||
Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 102)) {
|
||||
|
||||
object.testNumber = NSNumber(value: 22)
|
||||
object.testDecimal = NSDecimalNumber(string: "22")
|
||||
object.testString = "nil:TestEntity1:22"
|
||||
object.testData = ("nil:TestEntity1:22" as NSString).data(using: String.Encoding.utf8.rawValue)!
|
||||
object.testDate = self.dateFormatter.date(from: "2000-01-22T00:00:00Z")!
|
||||
}
|
||||
else {
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
return transaction.hasChanges
|
||||
},
|
||||
success: { (hasChanges) in
|
||||
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
},
|
||||
failure: { _ in
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
}
|
||||
)
|
||||
self.waitAndCheckExpectations()
|
||||
}
|
||||
}
|
||||
@@ -409,31 +409,31 @@ class ListObserverTests: BaseTestDataTestCase {
|
||||
}
|
||||
)
|
||||
let saveExpectation = self.expectation(description: "save")
|
||||
stack.beginAsynchronous { (transaction) in
|
||||
|
||||
if let object = transaction.fetchOne(
|
||||
From<TestEntity1>(),
|
||||
Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 102)) {
|
||||
stack.perform(
|
||||
asynchronous: { (transaction) -> Bool in
|
||||
|
||||
object.testBoolean = NSNumber(value: true)
|
||||
}
|
||||
else {
|
||||
if let object = transaction.fetchOne(
|
||||
From<TestEntity1>(),
|
||||
Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 102)) {
|
||||
|
||||
object.testBoolean = NSNumber(value: true)
|
||||
}
|
||||
else {
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
return transaction.hasChanges
|
||||
},
|
||||
success: { (hasChanges) in
|
||||
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
},
|
||||
failure: { _ in
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
transaction.commit { (result) in
|
||||
|
||||
switch result {
|
||||
|
||||
case .success(let hasChanges):
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
|
||||
case .failure:
|
||||
XCTFail()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
self.waitAndCheckExpectations()
|
||||
}
|
||||
}
|
||||
@@ -544,25 +544,25 @@ class ListObserverTests: BaseTestDataTestCase {
|
||||
}
|
||||
)
|
||||
let saveExpectation = self.expectation(description: "save")
|
||||
stack.beginAsynchronous { (transaction) in
|
||||
|
||||
transaction.deleteAll(
|
||||
From<TestEntity1>(),
|
||||
Where(#keyPath(TestEntity1.testBoolean), isEqualTo: false)
|
||||
)
|
||||
transaction.commit { (result) in
|
||||
stack.perform(
|
||||
asynchronous: { (transaction) -> Bool in
|
||||
|
||||
switch result {
|
||||
|
||||
case .success(let hasChanges):
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
|
||||
case .failure:
|
||||
XCTFail()
|
||||
}
|
||||
transaction.deleteAll(
|
||||
From<TestEntity1>(),
|
||||
Where(#keyPath(TestEntity1.testBoolean), isEqualTo: false)
|
||||
)
|
||||
return transaction.hasChanges
|
||||
},
|
||||
success: { (hasChanges) in
|
||||
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
},
|
||||
failure: { _ in
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
}
|
||||
)
|
||||
self.waitAndCheckExpectations()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,29 +125,29 @@ import CoreStore
|
||||
}
|
||||
)
|
||||
let saveExpectation = self.expectation(description: "save")
|
||||
stack.beginAsynchronous { (transaction) in
|
||||
|
||||
guard let object = transaction.edit(object) else {
|
||||
stack.perform(
|
||||
asynchronous: { (transaction) -> Bool in
|
||||
|
||||
guard let object = transaction.edit(object) else {
|
||||
|
||||
XCTFail()
|
||||
try transaction.cancel()
|
||||
}
|
||||
object.testNumber = NSNumber(value: 10)
|
||||
object.testString = "nil:TestEntity1:10"
|
||||
|
||||
return transaction.hasChanges
|
||||
},
|
||||
success: { (hasChanges) in
|
||||
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
},
|
||||
failure: { _ in
|
||||
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
object.testNumber = NSNumber(value: 10)
|
||||
object.testString = "nil:TestEntity1:10"
|
||||
|
||||
transaction.commit { (result) in
|
||||
|
||||
switch result {
|
||||
|
||||
case .success(let hasChanges):
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
|
||||
case .failure:
|
||||
XCTFail()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
self.waitAndCheckExpectations()
|
||||
}
|
||||
}
|
||||
@@ -193,29 +193,29 @@ import CoreStore
|
||||
}
|
||||
)
|
||||
let saveExpectation = self.expectation(description: "save")
|
||||
stack.beginAsynchronous { (transaction) in
|
||||
|
||||
guard let object = transaction.edit(object) else {
|
||||
stack.perform(
|
||||
asynchronous: { (transaction) -> Bool in
|
||||
|
||||
guard let object = transaction.edit(object) else {
|
||||
|
||||
XCTFail()
|
||||
try transaction.cancel()
|
||||
}
|
||||
transaction.delete(object)
|
||||
|
||||
return transaction.hasChanges
|
||||
},
|
||||
success: { (hasChanges) in
|
||||
|
||||
XCTAssertTrue(hasChanges)
|
||||
XCTAssertTrue(monitor.isObjectDeleted)
|
||||
saveExpectation.fulfill()
|
||||
},
|
||||
failure: { _ in
|
||||
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
transaction.delete(object)
|
||||
|
||||
transaction.commit { (result) in
|
||||
|
||||
switch result {
|
||||
|
||||
case .success(let hasChanges):
|
||||
XCTAssertTrue(hasChanges)
|
||||
XCTAssertTrue(monitor.isObjectDeleted)
|
||||
saveExpectation.fulfill()
|
||||
|
||||
case .failure:
|
||||
XCTFail()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
self.waitAndCheckExpectations()
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -33,7 +33,7 @@ public extension NSManagedObject {
|
||||
|
||||
/**
|
||||
Exposes a `FetchableSource` that can fetch sibling objects of this `NSManagedObject` instance. This may be the `DataStack`, a `BaseDataTransaction`, the `NSManagedObjectContext` itself, or `nil` if the obejct's parent is already deallocated.
|
||||
- Warning: Future implementations may change the instance returned by this method depending on the timing or condition that `fetchSource()` was called. Do not make assumptions that the instance will be a specific instance. If the `NSManagedObjectContext` instance is desired, use the `FetchableSource.internalContext()` method to get the correct instance. Also, do not assume that the `fetchSource()` and `querySource()` return the same instance all the time.
|
||||
- Warning: Future implementations may change the instance returned by this method depending on the timing or condition that `fetchSource()` was called. Do not make assumptions that the instance will be a specific instance. If the `NSManagedObjectContext` instance is desired, use the `FetchableSource.unsafeContext()` method to get the correct instance. Also, do not assume that the `fetchSource()` and `querySource()` return the same instance all the time.
|
||||
- returns: a `FetchableSource` that can fetch sibling objects of this `NSManagedObject` instance. This may be the `DataStack`, a `BaseDataTransaction`, the `NSManagedObjectContext` itself, or `nil` if the object's parent is already deallocated.
|
||||
*/
|
||||
@nonobjc
|
||||
@@ -56,7 +56,7 @@ public extension NSManagedObject {
|
||||
|
||||
/**
|
||||
Exposes a `QueryableSource` that can query attributes and aggregate values. This may be the `DataStack`, a `BaseDataTransaction`, the `NSManagedObjectContext` itself, or `nil` if the obejct's parent is already deallocated.
|
||||
- Warning: Future implementations may change the instance returned by this method depending on the timing or condition that `querySource()` was called. Do not make assumptions that the instance will be a specific instance. If the `NSManagedObjectContext` instance is desired, use the `QueryableSource.internalContext()` method to get the correct instance. Also, do not assume that the `fetchSource()` and `querySource()` return the same instance all the time.
|
||||
- Warning: Future implementations may change the instance returned by this method depending on the timing or condition that `querySource()` was called. Do not make assumptions that the instance will be a specific instance. If the `NSManagedObjectContext` instance is desired, use the `QueryableSource.unsafeContext()` method to get the correct instance. Also, do not assume that the `fetchSource()` and `querySource()` return the same instance all the time.
|
||||
- returns: a `QueryableSource` that can query attributes and aggregate values. This may be the `DataStack`, a `BaseDataTransaction`, the `NSManagedObjectContext` itself, or `nil` if the object's parent is already deallocated.
|
||||
*/
|
||||
@nonobjc
|
||||
|
||||
@@ -288,10 +288,19 @@ public extension NSError {
|
||||
|
||||
internal var isCoreDataMigrationError: Bool {
|
||||
|
||||
let code = self.code
|
||||
return (code == NSPersistentStoreIncompatibleVersionHashError
|
||||
|| code == NSMigrationMissingSourceModelError
|
||||
|| code == NSMigrationError)
|
||||
&& self.domain == NSCocoaErrorDomain
|
||||
guard self.domain == CocoaError.errorDomain else {
|
||||
|
||||
return false
|
||||
}
|
||||
switch CocoaError.Code(rawValue: self.code) {
|
||||
|
||||
case CocoaError.Code.persistentStoreIncompatibleVersionHash,
|
||||
CocoaError.Code.migrationMissingSourceModel,
|
||||
CocoaError.Code.migration:
|
||||
return true
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,8 +359,17 @@ extension BaseDataTransaction: FetchableSource, QueryableSource {
|
||||
/**
|
||||
The internal `NSManagedObjectContext` managed by this instance. Using this context directly should typically be avoided, and is provided by CoreStore only for extremely specialized cases.
|
||||
*/
|
||||
public func internalContext() -> NSManagedObjectContext {
|
||||
public func unsafeContext() -> NSManagedObjectContext {
|
||||
|
||||
return self.context
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
@available(*, deprecated: 4.0.0, renamed: "unsafeContext()")
|
||||
public func internalContext() -> NSManagedObjectContext {
|
||||
|
||||
return self.unsafeContext()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,8 +322,17 @@ extension DataStack: FetchableSource, QueryableSource {
|
||||
/**
|
||||
The internal `NSManagedObjectContext` managed by this instance. Using this context directly should typically be avoided, and is provided by CoreStore only for extremely specialized cases.
|
||||
*/
|
||||
public func internalContext() -> NSManagedObjectContext {
|
||||
public func unsafeContext() -> NSManagedObjectContext {
|
||||
|
||||
return self.mainContext
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
@available(*, deprecated: 4.0.0, renamed: "unsafeContext()")
|
||||
public func internalContext() -> NSManagedObjectContext {
|
||||
|
||||
return self.unsafeContext()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,5 +159,11 @@ public protocol FetchableSource: class {
|
||||
/**
|
||||
The internal `NSManagedObjectContext` managed by this `FetchableSource`. Using this context directly should typically be avoided, and is provided by CoreStore only for extremely specialized cases.
|
||||
*/
|
||||
func unsafeContext() -> NSManagedObjectContext
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
@available(*, deprecated: 4.0.0, renamed: "unsafeContext()")
|
||||
func internalContext() -> NSManagedObjectContext
|
||||
}
|
||||
|
||||
@@ -85,5 +85,11 @@ public protocol QueryableSource: class {
|
||||
/**
|
||||
The internal `NSManagedObjectContext` managed by this `QueryableSource`. Using this context directly should typically be avoided, and is provided by CoreStore only for extremely specialized cases.
|
||||
*/
|
||||
func unsafeContext() -> NSManagedObjectContext
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
@available(*, deprecated: 4.0.0, renamed: "unsafeContext()")
|
||||
func internalContext() -> NSManagedObjectContext
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ public protocol ImportableObject: class, NSObjectProtocol, AnyObject {
|
||||
func didInsert(from source: ImportSource, in transaction: BaseDataTransaction) throws
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
// MARK: Obsolete (`deprecated` only for reference, please use new methods)
|
||||
|
||||
@available(*, deprecated: 3.0.0, renamed: "shouldInsert(from:in:)")
|
||||
static func shouldInsertFromImportSource(_ source: ImportSource, inTransaction transaction: BaseDataTransaction) -> Bool
|
||||
@@ -93,13 +93,15 @@ public extension ImportableObject {
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
// MARK: Obsolete
|
||||
|
||||
@available(*, obsoleted: 4.0.0, renamed: "shouldInsert(from:in:)")
|
||||
static func shouldInsertFromImportSource(_ source: ImportSource, inTransaction transaction: BaseDataTransaction) -> Bool {
|
||||
|
||||
return Self.shouldInsert(from: source, in: transaction)
|
||||
}
|
||||
|
||||
@available(*, obsoleted: 4.0.0, renamed: "didInsert(from:in:)")
|
||||
func didInsertFromImportSource(_ source: ImportSource, inTransaction transaction: BaseDataTransaction) throws {
|
||||
|
||||
try self.didInsert(from: source, in: transaction)
|
||||
|
||||
@@ -116,7 +116,7 @@ public protocol ImportableUniqueObject: ImportableObject {
|
||||
func update(from source: ImportSource, in transaction: BaseDataTransaction) throws
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
// MARK: Obsolete (`deprecated` only for reference, please use new methods)
|
||||
|
||||
@available(*, deprecated: 3.0.0, renamed: "shouldInsert(from:in:)")
|
||||
static func shouldInsertFromImportSource(_ source: ImportSource, inTransaction transaction: BaseDataTransaction) -> Bool
|
||||
@@ -155,28 +155,33 @@ public extension ImportableUniqueObject {
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
// MARK: Obsolete
|
||||
|
||||
@available(*, obsoleted: 4.0.0, renamed: "shouldInsert(from:in:)")
|
||||
static func shouldInsertFromImportSource(_ source: ImportSource, inTransaction transaction: BaseDataTransaction) -> Bool {
|
||||
|
||||
return Self.shouldInsert(from: source, in: transaction)
|
||||
}
|
||||
|
||||
@available(*, obsoleted: 4.0.0, renamed: "shouldUpdate(from:in:)")
|
||||
static func shouldUpdateFromImportSource(_ source: ImportSource, inTransaction transaction: BaseDataTransaction) -> Bool {
|
||||
|
||||
return Self.shouldUpdate(from: source, in: transaction)
|
||||
}
|
||||
|
||||
@available(*, obsoleted: 4.0.0, renamed: "uniqueID(from:in:)")
|
||||
static func uniqueIDFromImportSource(_ source: ImportSource, inTransaction transaction: BaseDataTransaction) throws -> UniqueIDType? {
|
||||
|
||||
return try Self.uniqueID(from: source, in: transaction)
|
||||
}
|
||||
|
||||
@available(*, obsoleted: 4.0.0, renamed: "didInsert(from:in:)")
|
||||
func didInsertFromImportSource(_ source: ImportSource, inTransaction transaction: BaseDataTransaction) throws {
|
||||
|
||||
try self.didInsert(from: source, in: transaction)
|
||||
}
|
||||
|
||||
@available(*, obsoleted: 4.0.0, renamed: "update(from:in:)")
|
||||
func updateFromImportSource(_ source: ImportSource, inTransaction transaction: BaseDataTransaction) throws {
|
||||
|
||||
try self.update(from: source, in: transaction)
|
||||
|
||||
@@ -54,9 +54,14 @@ internal extension DispatchQueue {
|
||||
)
|
||||
}
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
@nonobjc
|
||||
internal func cs_isCurrentExecutionContext() -> Bool {
|
||||
|
||||
enum Static {
|
||||
|
||||
static let specificKey = DispatchSpecificKey<ObjectIdentifier>()
|
||||
}
|
||||
|
||||
let specific = ObjectIdentifier(self)
|
||||
|
||||
self.setSpecific(key: Static.specificKey, value: specific)
|
||||
@@ -89,9 +94,4 @@ internal extension DispatchQueue {
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private enum Static {
|
||||
|
||||
static let specificKey = DispatchSpecificKey<ObjectIdentifier>()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,10 +290,19 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource {
|
||||
// MARK: FetchableSource, QueryableSource
|
||||
|
||||
@nonobjc
|
||||
public func internalContext() -> NSManagedObjectContext {
|
||||
public func unsafeContext() -> NSManagedObjectContext {
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
@available(*, deprecated: 4.0.0, renamed: "unsafeContext()")
|
||||
public func internalContext() -> NSManagedObjectContext {
|
||||
|
||||
return self.unsafeContext()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ internal extension NSManagedObjectContext {
|
||||
|
||||
for objectID in updatedObjectIDs {
|
||||
|
||||
context?.object(with: objectID).willAccessValue(forKey: nil)
|
||||
context?.registeredObject(for: objectID)?.willAccessValue(forKey: nil)
|
||||
}
|
||||
}
|
||||
context?.mergeChanges(fromContextDidSave: note)
|
||||
|
||||
@@ -140,17 +140,15 @@ internal extension NSManagedObjectContext {
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
internal func saveSynchronously(waitForMerge: Bool) -> SaveResult {
|
||||
internal func saveSynchronously(waitForMerge: Bool) -> (hasChanges: Bool, error: CoreStoreError?) {
|
||||
|
||||
var result = SaveResult(hasChanges: false)
|
||||
|
||||
var result: (hasChanges: Bool, error: CoreStoreError?) = (false, nil)
|
||||
self.performAndWait {
|
||||
|
||||
guard self.hasChanges else {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
self.isSavingSynchronously = waitForMerge
|
||||
@@ -164,32 +162,24 @@ internal extension NSManagedObjectContext {
|
||||
saveError,
|
||||
"Failed to save \(cs_typeName(NSManagedObjectContext.self))."
|
||||
)
|
||||
result = SaveResult(saveError)
|
||||
result = (true, saveError)
|
||||
return
|
||||
}
|
||||
|
||||
if let parentContext = self.parent, self.shouldCascadeSavesToParent {
|
||||
|
||||
switch parentContext.saveSynchronously(waitForMerge: waitForMerge) {
|
||||
|
||||
case .success:
|
||||
result = SaveResult(hasChanges: true)
|
||||
|
||||
case .failure(let error):
|
||||
result = SaveResult(error)
|
||||
}
|
||||
let (_, error) = parentContext.saveSynchronously(waitForMerge: waitForMerge)
|
||||
result = (true, error)
|
||||
}
|
||||
else {
|
||||
|
||||
result = SaveResult(hasChanges: true)
|
||||
result = (true, nil)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
internal func saveAsynchronouslyWithCompletion(_ completion: @escaping ((_ result: SaveResult) -> Void) = { _ in }) {
|
||||
internal func saveAsynchronouslyWithCompletion(_ completion: @escaping (_ hasChanges: Bool, _ error: CoreStoreError?) -> Void = { _ in }) {
|
||||
|
||||
self.perform {
|
||||
|
||||
@@ -197,11 +187,10 @@ internal extension NSManagedObjectContext {
|
||||
|
||||
DispatchQueue.main.async {
|
||||
|
||||
completion(SaveResult(hasChanges: false))
|
||||
completion(false, nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
self.isSavingSynchronously = false
|
||||
@@ -216,21 +205,23 @@ internal extension NSManagedObjectContext {
|
||||
"Failed to save \(cs_typeName(NSManagedObjectContext.self))."
|
||||
)
|
||||
DispatchQueue.main.async {
|
||||
|
||||
completion(SaveResult(saveError))
|
||||
|
||||
completion(true, saveError)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if self.shouldCascadeSavesToParent, let parentContext = self.parent {
|
||||
|
||||
parentContext.saveAsynchronouslyWithCompletion(completion)
|
||||
parentContext.saveAsynchronouslyWithCompletion { (_, error) in
|
||||
|
||||
completion(true, error)
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
DispatchQueue.main.async {
|
||||
|
||||
completion(SaveResult(hasChanges: true))
|
||||
|
||||
completion(true, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,8 +48,7 @@ extension AsynchronousDataTransaction: CustomDebugStringConvertible, CoreStoreDe
|
||||
("context", self.context),
|
||||
("supportsUndo", self.supportsUndo),
|
||||
("bypassesQueueing", self.bypassesQueueing),
|
||||
("isCommitted", self.isCommitted),
|
||||
("result", self.result as Any)
|
||||
("isCommitted", self.isCommitted)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -614,6 +613,7 @@ extension OrderBy: CustomDebugStringConvertible, CoreStoreDebugStringConvertible
|
||||
|
||||
// MARK: - SaveResult
|
||||
|
||||
@available(*, deprecated: 4.0.0, message: "Use the new DataStack.perform(asynchronous:...) and DataStack.perform(synchronous:...) family of APIs")
|
||||
extension SaveResult: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
|
||||
|
||||
// MARK: CustomDebugStringConvertible
|
||||
@@ -824,8 +824,7 @@ extension SynchronousDataTransaction: CustomDebugStringConvertible, CoreStoreDeb
|
||||
("context", self.context),
|
||||
("supportsUndo", self.supportsUndo),
|
||||
("bypassesQueueing", self.bypassesQueueing),
|
||||
("isCommitted", self.isCommitted),
|
||||
("result", self.result as Any)
|
||||
("isCommitted", self.isCommitted)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ public extension CoreStore {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
@inline(__always)
|
||||
internal static func log(_ level: LogLevel, message: String, fileName: StaticString = #file, lineNumber: Int = #line, functionName: StaticString = #function) {
|
||||
|
||||
self.logger.log(
|
||||
@@ -49,6 +50,7 @@ public extension CoreStore {
|
||||
)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
internal static func log(_ error: CoreStoreError, _ message: String, fileName: StaticString = #file, lineNumber: Int = #line, functionName: StaticString = #function) {
|
||||
|
||||
self.logger.log(
|
||||
@@ -60,6 +62,7 @@ public extension CoreStore {
|
||||
)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
internal static func assert( _ condition: @autoclosure () -> Bool, _ message: String, fileName: StaticString = #file, lineNumber: Int = #line, functionName: StaticString = #function) {
|
||||
|
||||
self.logger.assert(
|
||||
@@ -71,6 +74,7 @@ public extension CoreStore {
|
||||
)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
internal static func abort(_ message: String, fileName: StaticString = #file, lineNumber: Int = #line, functionName: StaticString = #function) -> Never {
|
||||
|
||||
self.logger.abort(
|
||||
|
||||
@@ -43,29 +43,22 @@ public final class CSAsynchronousDataTransaction: CSBaseDataTransaction {
|
||||
- parameter completion: the block executed after the save completes. Success or failure is reported by the `CSSaveResult` argument of the block.
|
||||
*/
|
||||
@objc
|
||||
public func commitWithCompletion(_ completion: ((_ result: CSSaveResult) -> Void)?) {
|
||||
public func commitWithSuccess(_ success: (() -> Void)?, failure: ((CSError) -> Void)?) {
|
||||
|
||||
self.bridgeToSwift.commit { (result) in
|
||||
CoreStore.assert(
|
||||
self.bridgeToSwift.transactionQueue.cs_isCurrentExecutionContext(),
|
||||
"Attempted to commit a \(cs_typeName(self)) outside its designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
!self.bridgeToSwift.isCommitted,
|
||||
"Attempted to commit a \(cs_typeName(self)) more than once."
|
||||
)
|
||||
self.bridgeToSwift.autoCommit { (result) in
|
||||
|
||||
completion?(result.bridgeToObjectiveC)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Begins a child transaction synchronously where `NSManagedObject` creates, updates, and deletes can be made. This method should not be used after the `-commitWithCompletion:` method was already called once.
|
||||
|
||||
- parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
- returns: a `CSSaveResult` value indicating success or failure, or `nil` if the transaction was not comitted synchronously
|
||||
*/
|
||||
@objc
|
||||
@discardableResult
|
||||
public func beginSynchronous(_ closure: @escaping (_ transaction: CSSynchronousDataTransaction) -> Void) -> CSSaveResult? {
|
||||
|
||||
return bridge {
|
||||
|
||||
self.bridgeToSwift.beginSynchronous { (transaction) in
|
||||
switch result {
|
||||
|
||||
closure(transaction.bridgeToObjectiveC)
|
||||
case (_, nil): success?()
|
||||
case (_, let error?): failure?(error.bridgeToObjectiveC)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,6 +152,52 @@ public final class CSAsynchronousDataTransaction: CSBaseDataTransaction {
|
||||
|
||||
super.init(swiftValue as! AsynchronousDataTransaction)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
/**
|
||||
Saves the transaction changes. This method should not be used after the `-commitWithCompletion:` method was already called once.
|
||||
|
||||
- parameter completion: the block executed after the save completes. Success or failure is reported by the `CSSaveResult` argument of the block.
|
||||
*/
|
||||
@available(*, deprecated: 4.0.0, message: "Use the new -[CSAsynchronousDataTransaction commitWithSuccess:failure:] method.")
|
||||
@objc
|
||||
public func commitWithCompletion(_ completion: ((_ result: CSSaveResult) -> Void)?) {
|
||||
|
||||
CoreStore.assert(
|
||||
self.bridgeToSwift.transactionQueue.cs_isCurrentExecutionContext(),
|
||||
"Attempted to commit a \(cs_typeName(self)) outside its designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
!self.bridgeToSwift.isCommitted,
|
||||
"Attempted to commit a \(cs_typeName(self)) more than once."
|
||||
)
|
||||
self.bridgeToSwift.commit { (result) in
|
||||
|
||||
completion?(result.bridgeToObjectiveC)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Begins a child transaction synchronously where `NSManagedObject` creates, updates, and deletes can be made. This method should not be used after the `-commitWithCompletion:` method was already called once.
|
||||
|
||||
- parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
- returns: a `CSSaveResult` value indicating success or failure, or `nil` if the transaction was not comitted synchronously
|
||||
*/
|
||||
@available(*, deprecated: 4.0.0, message: "Secondary tasks spawned from CSAsynchronousDataTransactions and CSSynchronousDataTransactions are no longer supported. ")
|
||||
@objc
|
||||
@discardableResult
|
||||
public func beginSynchronous(_ closure: @escaping (_ transaction: CSSynchronousDataTransaction) -> Void) -> CSSaveResult? {
|
||||
|
||||
return bridge {
|
||||
|
||||
self.bridgeToSwift.beginSynchronous { (transaction) in
|
||||
|
||||
closure(transaction.bridgeToObjectiveC)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -38,10 +38,7 @@ public extension CSCoreStore {
|
||||
@objc
|
||||
public static func beginAsynchronous(_ closure: @escaping (_ transaction: CSAsynchronousDataTransaction) -> Void) {
|
||||
|
||||
return CoreStore.beginAsynchronous { (transaction) in
|
||||
|
||||
closure(transaction.bridgeToObjectiveC)
|
||||
}
|
||||
self.defaultStack.beginAsynchronous(closure)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,16 +48,9 @@ public extension CSCoreStore {
|
||||
- returns: a `CSSaveResult` value indicating success or failure, or `nil` if the transaction was not comitted synchronously
|
||||
*/
|
||||
@objc
|
||||
@discardableResult
|
||||
public static func beginSynchronous(_ closure: @escaping (_ transaction: CSSynchronousDataTransaction) -> Void) -> CSSaveResult? {
|
||||
public static func beginSynchronous(_ closure: @escaping (_ transaction: CSSynchronousDataTransaction) -> Void, error: NSErrorPointer) -> Bool {
|
||||
|
||||
return bridge {
|
||||
|
||||
CoreStore.beginSynchronous { (transaction) in
|
||||
|
||||
closure(transaction.bridgeToObjectiveC)
|
||||
}
|
||||
}
|
||||
return self.defaultStack.beginSynchronous(closure, error: error)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,4 +91,21 @@ public extension CSCoreStore {
|
||||
|
||||
CoreStore.refreshAndMergeAllObjects()
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, begins a transaction synchronously where `NSManagedObject` creates, updates, and deletes can be made.
|
||||
|
||||
- parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
- returns: a `CSSaveResult` value indicating success or failure, or `nil` if the transaction was not comitted synchronously
|
||||
*/
|
||||
@available(*, deprecated: 4.0.0, message: "Use the new +[CSCoreStore beginSynchronous:error:] API that reports failure using an error instance.")
|
||||
@objc
|
||||
@discardableResult
|
||||
public static func beginSynchronous(_ closure: @escaping (_ transaction: CSSynchronousDataTransaction) -> Void) -> CSSaveResult? {
|
||||
|
||||
return self.defaultStack.beginSynchronous(closure)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,27 +38,57 @@ public extension CSDataStack {
|
||||
@objc
|
||||
public func beginAsynchronous(_ closure: @escaping (_ transaction: CSAsynchronousDataTransaction) -> Void) {
|
||||
|
||||
return self.bridgeToSwift.beginAsynchronous { (transaction) in
|
||||
|
||||
closure(transaction.bridgeToObjectiveC)
|
||||
}
|
||||
self.bridgeToSwift.perform(
|
||||
asynchronous: { (transaction) in
|
||||
|
||||
let csTransaction = transaction.bridgeToObjectiveC
|
||||
closure(csTransaction)
|
||||
if !transaction.isCommitted && transaction.hasChanges {
|
||||
|
||||
CoreStore.log(
|
||||
.warning,
|
||||
message: "The closure for the \(cs_typeName(csTransaction)) completed without being committed. All changes made within the transaction were discarded."
|
||||
)
|
||||
}
|
||||
try transaction.cancel()
|
||||
},
|
||||
completion: { _ in }
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Begins a transaction synchronously where `NSManagedObject` creates, updates, and deletes can be made.
|
||||
|
||||
- parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
- returns: a `CSSaveResult` value indicating success or failure, or `nil` if the transaction was not comitted synchronously
|
||||
- parameter error: the `CSError` pointer that indicates the reason in case of an failure
|
||||
- returns: `YES` if the commit succeeded, `NO` if the commit failed. If `NO`, the `error` argument will hold error information.
|
||||
*/
|
||||
@objc
|
||||
@discardableResult
|
||||
public func beginSynchronous(_ closure: @escaping (_ transaction: CSSynchronousDataTransaction) -> Void) -> CSSaveResult? {
|
||||
public func beginSynchronous(_ closure: @escaping (_ transaction: CSSynchronousDataTransaction) -> Void, error: NSErrorPointer) -> Bool {
|
||||
|
||||
return bridge {
|
||||
return bridge(error) {
|
||||
|
||||
self.bridgeToSwift.beginSynchronous { (transaction) in
|
||||
do {
|
||||
|
||||
closure(transaction.bridgeToObjectiveC)
|
||||
try self.bridgeToSwift.perform(
|
||||
synchronous: { (transaction) in
|
||||
|
||||
let csTransaction = transaction.bridgeToObjectiveC
|
||||
closure(csTransaction)
|
||||
if !transaction.isCommitted && transaction.hasChanges {
|
||||
|
||||
CoreStore.log(
|
||||
.warning,
|
||||
message: "The closure for the \(cs_typeName(csTransaction)) completed without being committed. All changes made within the transaction were discarded."
|
||||
)
|
||||
}
|
||||
try transaction.cancel()
|
||||
}
|
||||
)
|
||||
}
|
||||
catch CoreStoreError.userCancelled {
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,4 +131,27 @@ public extension CSDataStack {
|
||||
|
||||
self.bridgeToSwift.refreshAndMergeAllObjects()
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
/**
|
||||
Begins a transaction synchronously where `NSManagedObject` creates, updates, and deletes can be made.
|
||||
|
||||
- parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
- returns: a `CSSaveResult` value indicating success or failure, or `nil` if the transaction was not comitted synchronously
|
||||
*/
|
||||
@available(*, deprecated: 4.0.0, message: "Use the new -[CSDataStack beginSynchronous:error:] API that reports failure using an error instance.")
|
||||
@objc
|
||||
@discardableResult
|
||||
public func beginSynchronous(_ closure: @escaping (_ transaction: CSSynchronousDataTransaction) -> Void) -> CSSaveResult? {
|
||||
|
||||
return bridge {
|
||||
|
||||
self.bridgeToSwift.beginSynchronous { (transaction) in
|
||||
|
||||
closure(transaction.bridgeToObjectiveC)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import CoreData
|
||||
|
||||
- SeeAlso: `SaveResult`
|
||||
*/
|
||||
@available(*, deprecated: 4.0.0, message: "Use APIs that report failures with `CSError`s instead.")
|
||||
@objc
|
||||
public final class CSSaveResult: NSObject, CoreStoreObjectiveCType {
|
||||
|
||||
@@ -173,6 +174,7 @@ public final class CSSaveResult: NSObject, CoreStoreObjectiveCType {
|
||||
|
||||
// MARK: - SaveResult
|
||||
|
||||
@available(*, deprecated: 4.0.0, message: "Use the new DataStack.perform(asynchronous:...) and DataStack.perform(synchronous:...) family of APIs")
|
||||
extension SaveResult: CoreStoreSwiftType {
|
||||
|
||||
// MARK: CoreStoreSwiftType
|
||||
|
||||
@@ -38,34 +38,19 @@ import CoreData
|
||||
public final class CSSynchronousDataTransaction: CSBaseDataTransaction {
|
||||
|
||||
/**
|
||||
Saves the transaction changes and waits for completion synchronously. This method should not be used after the `-commitAndWait` method was already called once.
|
||||
Saves the transaction changes and waits for completion synchronously. This method should not be used after the `-commitAndWaitWithError:` method was already called once.
|
||||
|
||||
- returns: a `CSSaveResult` containing the success or failure information
|
||||
- parameter error: the `CSError` pointer that indicates the reason in case of an failure
|
||||
- returns: `YES` if the commit succeeded, `NO` if the commit failed. If `NO`, the `error` argument will hold error information.
|
||||
*/
|
||||
@objc
|
||||
public func commitAndWait() -> CSSaveResult {
|
||||
public func commitAndWait(error: NSErrorPointer) -> Bool {
|
||||
|
||||
return bridge {
|
||||
return bridge(error) {
|
||||
|
||||
self.bridgeToSwift.commitAndWait()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Begins a child transaction synchronously where `NSManagedObject` creates, updates, and deletes can be made. This method should not be used after the `-commitAndWait` method was already called once.
|
||||
|
||||
- parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
- returns: a `CSSaveResult` value indicating success or failure, or `nil` if the transaction was not comitted synchronously
|
||||
*/
|
||||
@objc
|
||||
@discardableResult
|
||||
public func beginSynchronous(_ closure: @escaping (_ transaction: CSSynchronousDataTransaction) -> Void) -> CSSaveResult? {
|
||||
|
||||
return bridge {
|
||||
|
||||
self.bridgeToSwift.beginSynchronous { (transaction) in
|
||||
if case (_, let error?) = self.bridgeToSwift.context.saveSynchronously(waitForMerge: true) {
|
||||
|
||||
closure(transaction.bridgeToObjectiveC)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,6 +143,44 @@ public final class CSSynchronousDataTransaction: CSBaseDataTransaction {
|
||||
|
||||
super.init(swiftValue as! SynchronousDataTransaction)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
/**
|
||||
Saves the transaction changes and waits for completion synchronously. This method should not be used after the `-commitAndWait` method was already called once.
|
||||
|
||||
- returns: a `CSSaveResult` containing the success or failure information
|
||||
*/
|
||||
@available(*, deprecated: 4.0.0, message: "Use the new -[CSSynchronousDataTransaction commitAndWaitWithError:] method")
|
||||
@objc
|
||||
public func commitAndWait() -> CSSaveResult {
|
||||
|
||||
return bridge {
|
||||
|
||||
self.bridgeToSwift.commitAndWait()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Begins a child transaction synchronously where `NSManagedObject` creates, updates, and deletes can be made. This method should not be used after the `-commitAndWait` method was already called once.
|
||||
|
||||
- parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
- returns: a `CSSaveResult` value indicating success or failure, or `nil` if the transaction was not comitted synchronously
|
||||
*/
|
||||
@available(*, deprecated: 4.0.0, message: "Secondary tasks spawned from CSAsynchronousDataTransactions and CSSynchronousDataTransactions are no longer supported. ")
|
||||
@objc
|
||||
@discardableResult
|
||||
public func beginSynchronous(_ closure: @escaping (_ transaction: CSSynchronousDataTransaction) -> Void) -> CSSaveResult? {
|
||||
|
||||
return bridge {
|
||||
|
||||
self.bridgeToSwift.beginSynchronous { (transaction) in
|
||||
|
||||
closure(transaction.bridgeToObjectiveC)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -36,32 +36,43 @@ import CoreData
|
||||
*/
|
||||
@objc
|
||||
public final class CSUnsafeDataTransaction: CSBaseDataTransaction {
|
||||
|
||||
/**
|
||||
Saves the transaction changes asynchronously. For a `CSUnsafeDataTransaction`, 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 `CSSaveResult` argument of the block.
|
||||
- parameter completion: the block executed after the save completes. Success or failure is reported by the `error` argument of the block.
|
||||
*/
|
||||
@objc
|
||||
public func commit(_ completion: ((_ result: CSSaveResult) -> Void)?) {
|
||||
public func commitWithSuccess(_ success: (() -> Void)?, _ failure: ((CSError) -> Void)?) {
|
||||
|
||||
self.bridgeToSwift.commit { (result) in
|
||||
self.bridgeToSwift.context.saveAsynchronouslyWithCompletion { (_, error) in
|
||||
|
||||
completion?(result.bridgeToObjectiveC)
|
||||
if let error = error {
|
||||
|
||||
failure?(error.bridgeToObjectiveC)
|
||||
}
|
||||
else {
|
||||
|
||||
success?()
|
||||
}
|
||||
withExtendedLifetime(self, {})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Saves the transaction changes and waits for completion synchronously. For a `CSUnsafeDataTransaction`, multiple commits are allowed, although it is the developer's responsibility to ensure a reasonable leeway to prevent blocking the main thread.
|
||||
|
||||
- returns: a `CSSaveResult` containing the success or failure information
|
||||
- parameter error: the `CSError` pointer that indicates the reason in case of an failure
|
||||
- returns: `YES` if the commit succeeded, `NO` if the commit failed. If `NO`, the `error` argument will hold error information.
|
||||
*/
|
||||
@objc
|
||||
public func commitAndWait() -> CSSaveResult {
|
||||
public func commitAndWait(error: NSErrorPointer) -> Bool {
|
||||
|
||||
return bridge {
|
||||
return bridge(error) {
|
||||
|
||||
self.bridgeToSwift.commitAndWait()
|
||||
if case (_, let error?) = self.bridgeToSwift.context.saveSynchronously(waitForMerge: true) {
|
||||
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,7 +167,7 @@ public final class CSUnsafeDataTransaction: CSBaseDataTransaction {
|
||||
- that all saves will be done either through the `CSUnsafeDataTransaction`'s `-commit:` or `-commitAndWait` method, or by calling `-save:` manually on the context, its parent, and all other ancestor contexts if there are any.
|
||||
*/
|
||||
@objc
|
||||
public var internalContext: NSManagedObjectContext {
|
||||
public func unsafeContext() -> NSManagedObjectContext {
|
||||
|
||||
return self.bridgeToSwift.context
|
||||
}
|
||||
@@ -188,6 +199,58 @@ public final class CSUnsafeDataTransaction: CSBaseDataTransaction {
|
||||
|
||||
super.init(swiftValue as! UnsafeDataTransaction)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
@available(*, deprecated: 4.0.0, renamed: "unsafeContext()")
|
||||
@objc
|
||||
public var internalContext: NSManagedObjectContext {
|
||||
|
||||
return self.bridgeToSwift.context
|
||||
}
|
||||
|
||||
/**
|
||||
Saves the transaction changes asynchronously. For a `CSUnsafeDataTransaction`, 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 `CSSaveResult` argument of the block.
|
||||
*/
|
||||
@available(*, deprecated: 4.0.0, message: "Use the new -[CSUnsafeDataTransaction commitWithSuccess:failure:] method")
|
||||
@objc
|
||||
public func commit(_ completion: ((_ result: CSSaveResult) -> Void)?) {
|
||||
|
||||
self.bridgeToSwift.context.saveAsynchronouslyWithCompletion { (hasChanges, error) in
|
||||
|
||||
if let error = error {
|
||||
|
||||
completion?(SaveResult(error).bridgeToObjectiveC)
|
||||
}
|
||||
else {
|
||||
|
||||
completion?(SaveResult(hasChanges: hasChanges).bridgeToObjectiveC)
|
||||
}
|
||||
withExtendedLifetime(self, {})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Saves the transaction changes and waits for completion synchronously. For a `CSUnsafeDataTransaction`, multiple commits are allowed, although it is the developer's responsibility to ensure a reasonable leeway to prevent blocking the main thread.
|
||||
|
||||
- returns: a `CSSaveResult` containing the success or failure information
|
||||
*/
|
||||
@available(*, deprecated: 4.0.0, message: "Use the new -[CSUnsafeDataTransaction commitAndWaitWithError:] method")
|
||||
@objc
|
||||
public func commitAndWait() -> CSSaveResult {
|
||||
|
||||
return bridge { () -> SaveResult in
|
||||
|
||||
switch self.bridgeToSwift.context.saveSynchronously(waitForMerge: true) {
|
||||
|
||||
case (let hasChanges, nil): return SaveResult(hasChanges: hasChanges)
|
||||
case (_, let error?): return SaveResult(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -202,7 +202,7 @@ public final class ListMonitor<T: NSManagedObject>: Hashable {
|
||||
!self.isPendingRefetch || Thread.isMainThread,
|
||||
"Attempted to access a \(cs_typeName(self)) outside the main thread while a refetch is in progress."
|
||||
)
|
||||
return self.fetchedResultsController.dynamicCast().fetchedObjects ?? []
|
||||
return (self.fetchedResultsController.dynamicCast() as NSFetchedResultsController<T>).fetchedObjects ?? []
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,7 +213,7 @@ public final class ListMonitor<T: NSManagedObject>: Hashable {
|
||||
*/
|
||||
public func objectsInSection(_ section: Int) -> [T] {
|
||||
|
||||
return (self.sectionInfoAtIndex(section).objects as? [T]) ?? []
|
||||
return (self.sectionInfoAtIndex(section).objects as! [T]?) ?? []
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,7 +224,7 @@ public final class ListMonitor<T: NSManagedObject>: Hashable {
|
||||
*/
|
||||
public func objectsInSection(safeSectionIndex section: Int) -> [T]? {
|
||||
|
||||
return (self.sectionInfoAtIndex(safeSectionIndex: section)?.objects as? [T]) ?? []
|
||||
return self.sectionInfoAtIndex(safeSectionIndex: section)?.objects as! [T]?
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -371,7 +371,7 @@ public final class ListMonitor<T: NSManagedObject>: Hashable {
|
||||
!self.isPendingRefetch || Thread.isMainThread,
|
||||
"Attempted to access a \(cs_typeName(self)) outside the main thread while a refetch is in progress."
|
||||
)
|
||||
return (self.fetchedResultsController.dynamicCast().fetchedObjects ?? []).index(of: object)
|
||||
return (self.fetchedResultsController.dynamicCast() as NSFetchedResultsController<T>).fetchedObjects?.index(of: object)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -155,12 +155,9 @@ public extension CoreStore {
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
// MARK: Obsolete
|
||||
|
||||
/**
|
||||
Returns the `NSEntityDescription` for the specified `NSManagedObject` subclass from `defaultStack`'s model.
|
||||
*/
|
||||
@available(*, deprecated: 3.0.0, renamed: "entityDescription(for:)")
|
||||
@available(*, obsoleted: 4.0.0, renamed: "entityDescription(for:)")
|
||||
public static func entityDescriptionForType(_ type: NSManagedObject.Type) -> NSEntityDescription? {
|
||||
|
||||
return self.entityDescription(for: type)
|
||||
|
||||
@@ -529,15 +529,15 @@ public final class DataStack: Equatable {
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
// MARK: Obsolete
|
||||
|
||||
@available(*, deprecated: 3.0.0, renamed: "entityDescription(for:)")
|
||||
@available(*, obsoleted: 4.0.0, renamed: "entityDescription(for:)")
|
||||
public func entityDescriptionForType(_ type: NSManagedObject.Type) -> NSEntityDescription? {
|
||||
|
||||
return self.entityDescription(for: type)
|
||||
}
|
||||
|
||||
@available(*, deprecated: 3.0.0, renamed: "objectID(forURIRepresentation:)")
|
||||
@available(*, obsoleted: 4.0.0, renamed: "objectID(forURIRepresentation:)")
|
||||
public func objectIDForURIRepresentation(_ url: URL) -> NSManagedObjectID? {
|
||||
|
||||
return self.objectID(forURIRepresentation: url)
|
||||
|
||||
@@ -34,63 +34,51 @@ import CoreData
|
||||
*/
|
||||
public final class AsynchronousDataTransaction: BaseDataTransaction {
|
||||
|
||||
// MARK: - Result
|
||||
|
||||
public enum Result<T> {
|
||||
|
||||
case success(userInfo: T)
|
||||
case failure(error: CoreStoreError)
|
||||
|
||||
public var boolValue: Bool {
|
||||
|
||||
switch self {
|
||||
|
||||
case .success: return true
|
||||
case .failure: return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal init(userInfo: T) {
|
||||
|
||||
self = .success(userInfo: userInfo)
|
||||
}
|
||||
|
||||
internal init(error: CoreStoreError) {
|
||||
|
||||
self = .failure(error: error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: -
|
||||
|
||||
/**
|
||||
Cancels a transaction by throwing `CoreStoreError.userCancelled`.
|
||||
```
|
||||
try transaction.cancel()
|
||||
```
|
||||
- Important: Never use `try?` or `try!` on a `cancel()` call. Always use `try`. Using `try?` will swallow the cancellation and the transaction will proceed to commit as normal. Using `try!` will crash the app as `cancel()` will *always* throw an error.
|
||||
*/
|
||||
public func cancel() throws -> Never {
|
||||
|
||||
throw CoreStoreError.userCancelled
|
||||
}
|
||||
|
||||
/**
|
||||
Saves the transaction changes. This method should not be used after the `commit()` method was already called once.
|
||||
|
||||
- parameter completion: the block executed after the save completes. Success or failure is reported by the `SaveResult` argument of the block.
|
||||
*/
|
||||
public func commit(_ completion: @escaping (_ result: SaveResult) -> Void = { _ in }) {
|
||||
|
||||
CoreStore.assert(
|
||||
self.transactionQueue.cs_isCurrentExecutionContext(),
|
||||
"Attempted to commit a \(cs_typeName(self)) outside its designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
!self.isCommitted,
|
||||
"Attempted to commit a \(cs_typeName(self)) more than once."
|
||||
)
|
||||
|
||||
self.isCommitted = true
|
||||
let group = DispatchGroup()
|
||||
group.enter()
|
||||
self.context.saveAsynchronouslyWithCompletion { (result) -> Void in
|
||||
|
||||
self.result = result
|
||||
completion(result)
|
||||
group.leave()
|
||||
}
|
||||
group.wait()
|
||||
}
|
||||
|
||||
/**
|
||||
Begins a child transaction synchronously where NSManagedObject creates, updates, and deletes can be made. This method should not be used after the `commit()` method was already called once.
|
||||
|
||||
- parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
- returns: a `SaveResult` value indicating success or failure, or `nil` if the transaction was not comitted synchronously
|
||||
*/
|
||||
@discardableResult
|
||||
public func beginSynchronous(_ closure: @escaping (_ transaction: SynchronousDataTransaction) -> Void) -> SaveResult? {
|
||||
|
||||
CoreStore.assert(
|
||||
self.transactionQueue.cs_isCurrentExecutionContext(),
|
||||
"Attempted to begin a child transaction from a \(cs_typeName(self)) outside its designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
!self.isCommitted,
|
||||
"Attempted to begin a child transaction from an already committed \(cs_typeName(self))."
|
||||
)
|
||||
|
||||
return SynchronousDataTransaction(
|
||||
mainContext: self.context,
|
||||
queue: self.childTransactionQueue,
|
||||
closure: closure).performAndWait()
|
||||
}
|
||||
|
||||
|
||||
// MARK: BaseDataTransaction
|
||||
|
||||
@@ -193,47 +181,93 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal init(mainContext: NSManagedObjectContext, queue: DispatchQueue, closure: @escaping (_ transaction: AsynchronousDataTransaction) -> Void) {
|
||||
|
||||
self.closure = closure
|
||||
internal init(mainContext: NSManagedObjectContext, queue: DispatchQueue) {
|
||||
|
||||
super.init(mainContext: mainContext, queue: queue, supportsUndo: false, bypassesQueueing: false)
|
||||
}
|
||||
|
||||
internal func perform() {
|
||||
internal func autoCommit(_ completion: @escaping (_ hasChanges: Bool, _ error: CoreStoreError?) -> Void) {
|
||||
|
||||
self.transactionQueue.async {
|
||||
self.isCommitted = true
|
||||
let group = DispatchGroup()
|
||||
group.enter()
|
||||
self.context.saveAsynchronouslyWithCompletion { (result) -> Void in
|
||||
|
||||
self.closure(self)
|
||||
if !self.isCommitted && self.hasChanges {
|
||||
completion(result.0, result.1)
|
||||
self.result = result
|
||||
group.leave()
|
||||
}
|
||||
group.wait()
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
/**
|
||||
Saves the transaction changes. This method should not be used after the `commit()` method was already called once.
|
||||
|
||||
- parameter completion: the block executed after the save completes. Success or failure is reported by the `SaveResult` argument of the block.
|
||||
*/
|
||||
@available(*, deprecated: 4.0.0, message: "Use the new auto-commiting methods `DataStack.perform(asynchronous:completion:)` or `DataStack.perform(asynchronous:success:failure:)`. Please read the documentation on the behavior of the new methods.")
|
||||
public func commit(_ completion: @escaping (_ result: SaveResult) -> Void = { _ in }) {
|
||||
|
||||
CoreStore.assert(
|
||||
self.transactionQueue.cs_isCurrentExecutionContext(),
|
||||
"Attempted to commit a \(cs_typeName(self)) outside its designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
!self.isCommitted,
|
||||
"Attempted to commit a \(cs_typeName(self)) more than once."
|
||||
)
|
||||
self.autoCommit { (result) in
|
||||
|
||||
switch result {
|
||||
|
||||
CoreStore.log(
|
||||
.warning,
|
||||
message: "The closure for the \(cs_typeName(self)) completed without being committed. All changes made within the transaction were discarded."
|
||||
)
|
||||
case (let hasChanges, nil): completion(SaveResult(hasChanges: hasChanges))
|
||||
case (_, let error?): completion(SaveResult(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal func performAndWait() -> SaveResult? {
|
||||
/**
|
||||
Begins a child transaction synchronously where NSManagedObject creates, updates, and deletes can be made. This method should not be used after the `commit()` method was already called once.
|
||||
|
||||
- parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
- returns: a `SaveResult` value indicating success or failure, or `nil` if the transaction was not comitted synchronously
|
||||
*/
|
||||
@available(*, deprecated: 4.0.0, message: "Secondary tasks spawned from AsynchronousDataTransactions and SynchronousDataTransactions are no longer supported. ")
|
||||
@discardableResult
|
||||
public func beginSynchronous(_ closure: @escaping (_ transaction: SynchronousDataTransaction) -> Void) -> SaveResult? {
|
||||
|
||||
self.transactionQueue.sync {
|
||||
CoreStore.assert(
|
||||
self.transactionQueue.cs_isCurrentExecutionContext(),
|
||||
"Attempted to begin a child transaction from a \(cs_typeName(self)) outside its designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
!self.isCommitted,
|
||||
"Attempted to begin a child transaction from an already committed \(cs_typeName(self))."
|
||||
)
|
||||
let childTransaction = SynchronousDataTransaction(
|
||||
mainContext: self.context,
|
||||
queue: self.childTransactionQueue
|
||||
)
|
||||
childTransaction.transactionQueue.cs_sync {
|
||||
|
||||
self.closure(self)
|
||||
closure(childTransaction)
|
||||
|
||||
if !self.isCommitted && self.hasChanges {
|
||||
if !childTransaction.isCommitted && childTransaction.hasChanges {
|
||||
|
||||
CoreStore.log(
|
||||
.warning,
|
||||
message: "The closure for the \(cs_typeName(self)) completed without being committed. All changes made within the transaction were discarded."
|
||||
message: "The closure for the \(cs_typeName(childTransaction)) completed without being committed. All changes made within the transaction were discarded."
|
||||
)
|
||||
}
|
||||
}
|
||||
return self.result
|
||||
switch childTransaction.result {
|
||||
|
||||
case nil: return nil
|
||||
case (let hasChanges, nil)?: return SaveResult(hasChanges: hasChanges)
|
||||
case (_, let error?)?: return SaveResult(error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let closure: (_ transaction: AsynchronousDataTransaction) -> Void
|
||||
}
|
||||
|
||||
@@ -455,7 +455,7 @@ public /*abstract*/ class BaseDataTransaction {
|
||||
internal let supportsUndo: Bool
|
||||
internal let bypassesQueueing: Bool
|
||||
internal var isCommitted = false
|
||||
internal var result: SaveResult?
|
||||
internal var result: (hasChanges: Bool, error: CoreStoreError?)?
|
||||
|
||||
internal init(mainContext: NSManagedObjectContext, queue: DispatchQueue, supportsUndo: Bool, bypassesQueueing: Bool) {
|
||||
|
||||
|
||||
@@ -31,25 +31,39 @@ import Foundation
|
||||
public extension CoreStore {
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, begins a transaction asynchronously where `NSManagedObject` creates, updates, and deletes can be made.
|
||||
Using the `defaultStack`, performs a transaction asynchronously where `NSManagedObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. On success, the value returned from closure will be the wrapped as `TransactionResult.success(userInfo: T)` in the `completion`'s `TransactionResult<T>`. Any errors thrown from inside the `task` will be reported as `TransactionResult.failure(error: Error)`. To cancel/rollback changes, call `transaction.cancel()`, which throws a `CoreStoreError.userCancelled`.
|
||||
|
||||
- parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
- parameter task: the asynchronous closure where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
- parameter completion: the closure executed after the save completes. The `TransactionResult` argument of the closure will either wrap the return value of `task`, or any uncaught errors thrown from within `task`. Cancelled `task`s will be indicated by `CoreStoreError.userCancelled`. Custom errors thrown by the user will be wrapped in `CoreStoreError.userError(error: Error)`.
|
||||
*/
|
||||
public static func beginAsynchronous(_ closure: @escaping (_ transaction: AsynchronousDataTransaction) -> Void) {
|
||||
public static func perform<T>(asynchronous task: @escaping (_ transaction: AsynchronousDataTransaction) throws -> T, completion: @escaping (AsynchronousDataTransaction.Result<T>) -> Void) {
|
||||
|
||||
self.defaultStack.beginAsynchronous(closure)
|
||||
self.defaultStack.perform(asynchronous: task, completion: completion)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, begins a transaction asynchronously where `NSManagedObject` creates, updates, and deletes can be made.
|
||||
Using the `defaultStack`, performs a transaction asynchronously where `NSManagedObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. On success, the value returned from closure will be the argument of the `success` closure. Any errors thrown from inside the `task` will be wrapped in a `CoreStoreError` and reported in the `failure` closure. To cancel/rollback changes, call `transaction.cancel()`, which throws a `CoreStoreError.userCancelled`.
|
||||
|
||||
- parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
- returns: a `SaveResult` value indicating success or failure, or `nil` if the transaction was not comitted synchronously
|
||||
- parameter task: the asynchronous closure where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
- parameter success: the closure executed after the save succeeds. The `T` argument of the closure will be the value returned from `task`.
|
||||
- parameter failure: the closure executed if the save fails or if any errors are thrown within `task`. Cancelled `task`s will be indicated by `CoreStoreError.userCancelled`. Custom errors thrown by the user will be wrapped in `CoreStoreError.userError(error: Error)`.
|
||||
*/
|
||||
@discardableResult
|
||||
public static func beginSynchronous(_ closure: @escaping (_ transaction: SynchronousDataTransaction) -> Void) -> SaveResult? {
|
||||
public static func perform<T>(asynchronous task: @escaping (_ transaction: AsynchronousDataTransaction) throws -> T, success: @escaping (T) -> Void, failure: @escaping (CoreStoreError) -> Void) {
|
||||
|
||||
return self.defaultStack.beginSynchronous(closure)
|
||||
self.defaultStack.perform(asynchronous: task, success: success, failure: failure)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, performs a transaction synchronously where `NSManagedObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. On success, the value returned from closure will be the return value of `perform(synchronous:)`. Any errors thrown from inside the `task` will be rethrown from `perform(synchronous:)`. To cancel/rollback changes, call `transaction.cancel()`, which throws a `CoreStoreError.userCancelled`.
|
||||
|
||||
- parameter task: the synchronous non-escaping closure where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
- parameter waitForAllObservers: When `true`, this method waits for all observers to be notified of the changes before returning. This results in more predictable data update order, but may risk triggering deadlocks. When `false`, this method does not wait for observers to be notified of the changes before returning. This results in lower risk for deadlocks, but the updated data may not have been propagated to the `DataStack` after returning. Defaults to `true`.
|
||||
- throws: a `CoreStoreError` value indicating the failure. Cancelled `task`s will be indicated by `CoreStoreError.userCancelled`. Custom errors thrown by the user will be wrapped in `CoreStoreError.userError(error: Error)`.
|
||||
- returns: the value returned from `task`
|
||||
*/
|
||||
public static func perform<T>(synchronous task: ((_ transaction: SynchronousDataTransaction) throws -> T), waitForAllObservers: Bool = true) throws -> T {
|
||||
|
||||
return try self.defaultStack.perform(synchronous: task, waitForAllObservers: waitForAllObservers)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,4 +84,31 @@ public extension CoreStore {
|
||||
|
||||
self.defaultStack.refreshAndMergeAllObjects()
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, begins a transaction asynchronously where `NSManagedObject` creates, updates, and deletes can be made.
|
||||
|
||||
- parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
*/
|
||||
@available(*, deprecated: 4.0.0, message: "Use the new auto-commiting methods `perform(asynchronous:completion:)` or `perform(asynchronous:success:failure:)`. Please read the documentation on the behavior of the new methods.")
|
||||
public static func beginAsynchronous(_ closure: @escaping (_ transaction: AsynchronousDataTransaction) -> Void) {
|
||||
|
||||
self.defaultStack.beginAsynchronous(closure)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, begins a transaction asynchronously where `NSManagedObject` creates, updates, and deletes can be made.
|
||||
|
||||
- parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
- returns: a `SaveResult` value indicating success or failure, or `nil` if the transaction was not comitted synchronously
|
||||
*/
|
||||
@available(*, deprecated: 4.0.0, message: "Use the new auto-commiting method `perform(synchronous:)`. Please read the documentation on the behavior of the new methods.")
|
||||
@discardableResult
|
||||
public static func beginSynchronous(_ closure: @escaping (_ transaction: SynchronousDataTransaction) -> Void) -> SaveResult? {
|
||||
|
||||
return self.defaultStack.beginSynchronous(closure)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,22 +31,33 @@ import CoreData
|
||||
|
||||
public extension DataStack {
|
||||
|
||||
|
||||
public func perform<T>(asynchronous task: @escaping (_ transaction: AsynchronousDataTransaction) throws -> T, completion: @escaping (TransactionResult<T>) -> Void) {
|
||||
/**
|
||||
Performs a transaction asynchronously where `NSManagedObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. On success, the value returned from closure will be the wrapped as `TransactionResult.success(userInfo: T)` in the `completion`'s `TransactionResult<T>`. Any errors thrown from inside the `task` will be reported as `TransactionResult.failure(error: Error)`. To cancel/rollback changes, call `transaction.cancel()`, which throws a `CoreStoreError.userCancelled`.
|
||||
|
||||
- parameter task: the asynchronous closure where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
- parameter completion: the closure executed after the save completes. The `TransactionResult` argument of the closure will either wrap the return value of `task`, or any uncaught errors thrown from within `task`. Cancelled `task`s will be indicated by `CoreStoreError.userCancelled`. Custom errors thrown by the user will be wrapped in `CoreStoreError.userError(error: Error)`.
|
||||
*/
|
||||
public func perform<T>(asynchronous task: @escaping (_ transaction: AsynchronousDataTransaction) throws -> T, completion: @escaping (AsynchronousDataTransaction.Result<T>) -> Void) {
|
||||
|
||||
self.perform(
|
||||
asynchronous: task,
|
||||
success: { completion(TransactionResult(userInfo: $0)) },
|
||||
failure: { completion(TransactionResult(error: $0)) }
|
||||
success: { completion(.init(userInfo: $0)) },
|
||||
failure: { completion(.init(error: $0)) }
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Performs a transaction asynchronously where `NSManagedObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. On success, the value returned from closure will be the argument of the `success` closure. Any errors thrown from inside the `task` will be wrapped in a `CoreStoreError` and reported in the `failure` closure. To cancel/rollback changes, call `transaction.cancel()`, which throws a `CoreStoreError.userCancelled`.
|
||||
|
||||
- parameter task: the asynchronous closure where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
- parameter success: the closure executed after the save succeeds. The `T` argument of the closure will be the value returned from `task`.
|
||||
- parameter failure: the closure executed if the save fails or if any errors are thrown within `task`. Cancelled `task`s will be indicated by `CoreStoreError.userCancelled`. Custom errors thrown by the user will be wrapped in `CoreStoreError.userError(error: Error)`.
|
||||
*/
|
||||
public func perform<T>(asynchronous task: @escaping (_ transaction: AsynchronousDataTransaction) throws -> T, success: @escaping (T) -> Void, failure: @escaping (CoreStoreError) -> Void) {
|
||||
|
||||
let transaction = AsynchronousDataTransaction(
|
||||
mainContext: self.rootSavingContext,
|
||||
queue: self.childTransactionQueue,
|
||||
closure: { _ in }
|
||||
queue: self.childTransactionQueue
|
||||
)
|
||||
transaction.transactionQueue.cs_async {
|
||||
|
||||
@@ -65,30 +76,40 @@ public extension DataStack {
|
||||
DispatchQueue.main.async { failure(.userError(error: error)) }
|
||||
return
|
||||
}
|
||||
transaction.commit { (result) in
|
||||
transaction.autoCommit { (_, error) in
|
||||
|
||||
switch result {
|
||||
if let error = error {
|
||||
|
||||
case .success: success(userInfo)
|
||||
case .failure(let error): failure(error)
|
||||
failure(error)
|
||||
}
|
||||
else {
|
||||
|
||||
success(userInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func perform<T>(synchronous task: ((_ transaction: SynchronousDataTransaction) throws -> T), waitForObserverNotifications: Bool = true) throws -> T {
|
||||
/**
|
||||
Performs a transaction synchronously where `NSManagedObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. On success, the value returned from closure will be the return value of `perform(synchronous:)`. Any errors thrown from inside the `task` will be rethrown from `perform(synchronous:)`. To cancel/rollback changes, call `transaction.cancel()`, which throws a `CoreStoreError.userCancelled`.
|
||||
|
||||
- parameter task: the synchronous non-escaping closure where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
- parameter waitForAllObservers: When `true`, this method waits for all observers to be notified of the changes before returning. This results in more predictable data update order, but may risk triggering deadlocks. When `false`, this method does not wait for observers to be notified of the changes before returning. This results in lower risk for deadlocks, but the updated data may not have been propagated to the `DataStack` after returning. Defaults to `true`.
|
||||
- throws: a `CoreStoreError` value indicating the failure. Cancelled `task`s will be indicated by `CoreStoreError.userCancelled`. Custom errors thrown by the user will be wrapped in `CoreStoreError.userError(error: Error)`.
|
||||
- returns: the value returned from `task`
|
||||
*/
|
||||
public func perform<T>(synchronous task: ((_ transaction: SynchronousDataTransaction) throws -> T), waitForAllObservers: Bool = true) throws -> T {
|
||||
|
||||
let transaction = SynchronousDataTransaction(
|
||||
mainContext: self.rootSavingContext,
|
||||
queue: self.childTransactionQueue,
|
||||
closure: { _ in }
|
||||
queue: self.childTransactionQueue
|
||||
)
|
||||
return try transaction.transactionQueue.cs_sync {
|
||||
|
||||
let userInfo: T
|
||||
do {
|
||||
|
||||
userInfo = try task(transaction)
|
||||
userInfo = try withoutActuallyEscaping(task, do: { try $0(transaction) })
|
||||
}
|
||||
catch let error as CoreStoreError {
|
||||
|
||||
@@ -98,46 +119,17 @@ public extension DataStack {
|
||||
|
||||
throw CoreStoreError.userError(error: error)
|
||||
}
|
||||
let result = waitForObserverNotifications
|
||||
? transaction.commitAndWait()
|
||||
: transaction.commit()
|
||||
switch result {
|
||||
if case (_, let error?) = transaction.autoCommit(waitForMerge: waitForAllObservers) {
|
||||
|
||||
case .success: return userInfo
|
||||
case .failure(let error): throw error
|
||||
throw error
|
||||
}
|
||||
else {
|
||||
|
||||
return userInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Begins a transaction asynchronously where `NSManagedObject` creates, updates, and deletes can be made.
|
||||
|
||||
- parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
*/
|
||||
public func beginAsynchronous(_ closure: @escaping (_ transaction: AsynchronousDataTransaction) -> Void) {
|
||||
|
||||
AsynchronousDataTransaction(
|
||||
mainContext: self.rootSavingContext,
|
||||
queue: self.childTransactionQueue,
|
||||
closure: closure).perform()
|
||||
}
|
||||
|
||||
/**
|
||||
Begins a transaction synchronously where `NSManagedObject` creates, updates, and deletes can be made.
|
||||
|
||||
- parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
- returns: a `SaveResult` value indicating success or failure, or `nil` if the transaction was not comitted synchronously
|
||||
*/
|
||||
@discardableResult
|
||||
public func beginSynchronous(_ closure: @escaping (_ transaction: SynchronousDataTransaction) -> Void) -> SaveResult? {
|
||||
|
||||
return SynchronousDataTransaction(
|
||||
mainContext: self.rootSavingContext,
|
||||
queue: self.childTransactionQueue,
|
||||
closure: closure).performAndWait()
|
||||
}
|
||||
|
||||
/**
|
||||
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.
|
||||
|
||||
@@ -164,4 +156,67 @@ public extension DataStack {
|
||||
)
|
||||
self.mainContext.refreshAndMergeAllObjects()
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
/**
|
||||
Begins a transaction asynchronously where `NSManagedObject` creates, updates, and deletes can be made.
|
||||
|
||||
- parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
*/
|
||||
@available(*, deprecated: 4.0.0, message: "Use the new auto-commiting methods `perform(asynchronous:completion:)` or `perform(asynchronous:success:failure:)`. Please read the documentation on the behavior of the new methods.")
|
||||
public func beginAsynchronous(_ closure: @escaping (_ transaction: AsynchronousDataTransaction) -> Void) {
|
||||
|
||||
let transaction = AsynchronousDataTransaction(
|
||||
mainContext: self.rootSavingContext,
|
||||
queue: self.childTransactionQueue
|
||||
)
|
||||
transaction.transactionQueue.cs_async {
|
||||
|
||||
closure(transaction)
|
||||
|
||||
if !transaction.isCommitted && transaction.hasChanges {
|
||||
|
||||
CoreStore.log(
|
||||
.warning,
|
||||
message: "The closure for the \(cs_typeName(transaction)) completed without being committed. All changes made within the transaction were discarded."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Begins a transaction synchronously where `NSManagedObject` creates, updates, and deletes can be made.
|
||||
|
||||
- parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
- returns: a `SaveResult` value indicating success or failure, or `nil` if the transaction was not comitted synchronously
|
||||
*/
|
||||
@available(*, deprecated: 4.0.0, message: "Use the new auto-commiting method `perform(synchronous:)`. Please read the documentation on the behavior of the new methods.")
|
||||
@discardableResult
|
||||
public func beginSynchronous(_ closure: @escaping (_ transaction: SynchronousDataTransaction) -> Void) -> SaveResult? {
|
||||
|
||||
let transaction = SynchronousDataTransaction(
|
||||
mainContext: self.rootSavingContext,
|
||||
queue: self.childTransactionQueue
|
||||
)
|
||||
transaction.transactionQueue.cs_sync {
|
||||
|
||||
closure(transaction)
|
||||
|
||||
if !transaction.isCommitted && transaction.hasChanges {
|
||||
|
||||
CoreStore.log(
|
||||
.warning,
|
||||
message: "The closure for the \(cs_typeName(transaction)) completed without being committed. All changes made within the transaction were discarded."
|
||||
)
|
||||
}
|
||||
}
|
||||
switch transaction.result {
|
||||
|
||||
case nil: return nil
|
||||
case (let hasChanges, nil)?: return SaveResult(hasChanges: hasChanges)
|
||||
case (_, let error?)?: return SaveResult(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ import Foundation
|
||||
}
|
||||
```
|
||||
*/
|
||||
@available(*, deprecated: 4.0.0, message: "Use the new DataStack.perform(asynchronous:...) and DataStack.perform(synchronous:...) family of APIs")
|
||||
public enum SaveResult: Hashable {
|
||||
|
||||
/**
|
||||
|
||||
@@ -34,83 +34,18 @@ import CoreData
|
||||
*/
|
||||
public final class SynchronousDataTransaction: BaseDataTransaction {
|
||||
|
||||
/**
|
||||
Cancels a transaction by throwing `CoreStoreError.userCancelled`.
|
||||
```
|
||||
try transaction.cancel()
|
||||
```
|
||||
- Important: Never use `try?` or `try!` on a `cancel()` call. Always use `try`. Using `try?` will swallow the cancellation and the transaction will proceed to commit as normal. Using `try!` will crash the app as `cancel()` will *always* throw an error.
|
||||
*/
|
||||
public func cancel() throws -> Never {
|
||||
|
||||
throw CoreStoreError.userCancelled
|
||||
}
|
||||
|
||||
/**
|
||||
Saves the transaction changes and waits for completion synchronously. This method should not be used after the `commit()` or `commitAndWait()` method was already called once.
|
||||
- Important: Unlike `SynchronousDataTransaction.commit()`, this method waits for all observers to be notified of the changes before returning. This results in more predictable data update order, but may risk triggering deadlocks.
|
||||
|
||||
- returns: a `SaveResult` containing the success or failure information
|
||||
*/
|
||||
public func commitAndWait() -> SaveResult {
|
||||
|
||||
CoreStore.assert(
|
||||
self.transactionQueue.cs_isCurrentExecutionContext(),
|
||||
"Attempted to commit a \(cs_typeName(self)) outside its designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
!self.isCommitted,
|
||||
"Attempted to commit a \(cs_typeName(self)) more than once."
|
||||
)
|
||||
|
||||
self.isCommitted = true
|
||||
|
||||
let result = self.context.saveSynchronously(waitForMerge: true)
|
||||
self.result = result
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
Saves the transaction changes and waits for completion synchronously. This method should not be used after the `commit()` or `commitAndWait()` method was already called once.
|
||||
- Important: Unlike `SynchronousDataTransaction.commitAndWait()`, this method does not wait for observers to be notified of the changes before returning. This results in lower risk for deadlocks, but the updated data may not have been propagated to the `DataStack` after returning.
|
||||
|
||||
- returns: a `SaveResult` containing the success or failure information
|
||||
*/
|
||||
public func commit() -> SaveResult {
|
||||
|
||||
CoreStore.assert(
|
||||
self.transactionQueue.cs_isCurrentExecutionContext(),
|
||||
"Attempted to commit a \(cs_typeName(self)) outside its designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
!self.isCommitted,
|
||||
"Attempted to commit a \(cs_typeName(self)) more than once."
|
||||
)
|
||||
|
||||
self.isCommitted = true
|
||||
|
||||
let result = self.context.saveSynchronously(waitForMerge: false)
|
||||
self.result = result
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
Begins a child transaction synchronously where `NSManagedObject` creates, updates, and deletes can be made. This method should not be used after the `commit()` method was already called once.
|
||||
|
||||
- parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
- returns: a `SaveResult` value indicating success or failure, or `nil` if the transaction was not comitted synchronously
|
||||
*/
|
||||
@discardableResult
|
||||
public func beginSynchronous(_ closure: @escaping (_ transaction: SynchronousDataTransaction) -> Void) -> SaveResult? {
|
||||
|
||||
CoreStore.assert(
|
||||
self.transactionQueue.cs_isCurrentExecutionContext(),
|
||||
"Attempted to begin a child transaction from a \(cs_typeName(self)) outside its designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
!self.isCommitted,
|
||||
"Attempted to begin a child transaction from an already committed \(cs_typeName(self))."
|
||||
)
|
||||
|
||||
return SynchronousDataTransaction(
|
||||
mainContext: self.context,
|
||||
queue: self.childTransactionQueue,
|
||||
closure: closure).performAndWait()
|
||||
}
|
||||
|
||||
|
||||
// MARK: BaseDataTransaction
|
||||
|
||||
@@ -213,32 +148,109 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal init(mainContext: NSManagedObjectContext, queue: DispatchQueue, closure: @escaping (_ transaction: SynchronousDataTransaction) -> Void) {
|
||||
|
||||
self.closure = closure
|
||||
internal init(mainContext: NSManagedObjectContext, queue: DispatchQueue) {
|
||||
|
||||
super.init(mainContext: mainContext, queue: queue, supportsUndo: false, bypassesQueueing: false)
|
||||
}
|
||||
|
||||
internal func performAndWait() -> SaveResult? {
|
||||
internal func autoCommit(waitForMerge: Bool) -> (hasChanges: Bool, error: CoreStoreError?) {
|
||||
|
||||
self.transactionQueue.sync {
|
||||
|
||||
self.closure(self)
|
||||
|
||||
if !self.isCommitted && self.hasChanges {
|
||||
|
||||
CoreStore.log(
|
||||
.warning,
|
||||
message: "The closure for the \(cs_typeName(self)) completed without being committed. All changes made within the transaction were discarded."
|
||||
)
|
||||
}
|
||||
}
|
||||
return self.result
|
||||
self.isCommitted = true
|
||||
let result = self.context.saveSynchronously(waitForMerge: waitForMerge)
|
||||
self.result = result
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
// MARK: Deprecated
|
||||
|
||||
private let closure: (_ transaction: SynchronousDataTransaction) -> Void
|
||||
/**
|
||||
Saves the transaction changes and waits for completion synchronously. This method should not be used after the `commit()` or `commitAndWait()` method was already called once.
|
||||
- Important: Unlike `SynchronousDataTransaction.commit()`, this method waits for all observers to be notified of the changes before returning. This results in more predictable data update order, but may risk triggering deadlocks.
|
||||
|
||||
- returns: a `SaveResult` containing the success or failure information
|
||||
*/
|
||||
@available(*, deprecated: 4.0.0, message: "Use the new auto-commit method DataStack.perform(synchronous:waitForAllObservers:)")
|
||||
public func commitAndWait() -> SaveResult {
|
||||
|
||||
CoreStore.assert(
|
||||
self.transactionQueue.cs_isCurrentExecutionContext(),
|
||||
"Attempted to commit a \(cs_typeName(self)) outside its designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
!self.isCommitted,
|
||||
"Attempted to commit a \(cs_typeName(self)) more than once."
|
||||
)
|
||||
switch self.autoCommit(waitForMerge: true) {
|
||||
|
||||
case (let hasChanges, nil): return SaveResult(hasChanges: hasChanges)
|
||||
case (_, let error?): return SaveResult(error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Saves the transaction changes and waits for completion synchronously. This method should not be used after the `commit()` or `commitAndWait()` method was already called once.
|
||||
- Important: Unlike `SynchronousDataTransaction.commitAndWait()`, this method does not wait for observers to be notified of the changes before returning. This results in lower risk for deadlocks, but the updated data may not have been propagated to the `DataStack` after returning.
|
||||
|
||||
- returns: a `SaveResult` containing the success or failure information
|
||||
*/
|
||||
@available(*, deprecated: 4.0.0, message: "Use the new auto-commit method DataStack.perform(synchronous:waitForAllObservers:)")
|
||||
public func commit() -> SaveResult {
|
||||
|
||||
CoreStore.assert(
|
||||
self.transactionQueue.cs_isCurrentExecutionContext(),
|
||||
"Attempted to commit a \(cs_typeName(self)) outside its designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
!self.isCommitted,
|
||||
"Attempted to commit a \(cs_typeName(self)) more than once."
|
||||
)
|
||||
switch self.autoCommit(waitForMerge: false) {
|
||||
|
||||
case (let hasChanges, nil): return SaveResult(hasChanges: hasChanges)
|
||||
case (_, let error?): return SaveResult(error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Begins a child transaction synchronously where `NSManagedObject` creates, updates, and deletes can be made. This method should not be used after the `commit()` method was already called once.
|
||||
|
||||
- parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
- returns: a `SaveResult` value indicating success or failure, or `nil` if the transaction was not comitted synchronously
|
||||
*/
|
||||
@available(*, deprecated: 4.0.0, message: "Secondary tasks spawned from AsynchronousDataTransactions and SynchronousDataTransactions are no longer supported. ")
|
||||
@discardableResult
|
||||
public func beginSynchronous(_ closure: @escaping (_ transaction: SynchronousDataTransaction) -> Void) -> SaveResult? {
|
||||
|
||||
CoreStore.assert(
|
||||
self.transactionQueue.cs_isCurrentExecutionContext(),
|
||||
"Attempted to begin a child transaction from a \(cs_typeName(self)) outside its designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
!self.isCommitted,
|
||||
"Attempted to begin a child transaction from an already committed \(cs_typeName(self))."
|
||||
)
|
||||
let childTransaction = SynchronousDataTransaction(
|
||||
mainContext: self.context,
|
||||
queue: self.childTransactionQueue
|
||||
)
|
||||
childTransaction.transactionQueue.cs_sync {
|
||||
|
||||
closure(childTransaction)
|
||||
|
||||
if !childTransaction.isCommitted && childTransaction.hasChanges {
|
||||
|
||||
CoreStore.log(
|
||||
.warning,
|
||||
message: "The closure for the \(cs_typeName(childTransaction)) completed without being committed. All changes made within the transaction were discarded."
|
||||
)
|
||||
}
|
||||
}
|
||||
switch childTransaction.result {
|
||||
|
||||
case nil: return nil
|
||||
case (let hasChanges, nil)?: return SaveResult(hasChanges: hasChanges)
|
||||
case (_, let error?)?: return SaveResult(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
//
|
||||
// TransactionResult.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2017 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
|
||||
|
||||
|
||||
// MARK: - TransactionResult
|
||||
|
||||
public enum TransactionResult<T> {
|
||||
|
||||
case success(T)
|
||||
|
||||
case failure(CoreStoreError)
|
||||
|
||||
public var boolValue: Bool {
|
||||
|
||||
switch self {
|
||||
|
||||
case .success: return true
|
||||
case .failure: return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal init(userInfo: T) {
|
||||
|
||||
self = .success(userInfo)
|
||||
}
|
||||
|
||||
internal init(error: CoreStoreError) {
|
||||
|
||||
self = .failure(error)
|
||||
}
|
||||
}
|
||||
@@ -34,30 +34,33 @@ import CoreData
|
||||
*/
|
||||
public final class UnsafeDataTransaction: BaseDataTransaction {
|
||||
|
||||
// MARK: -
|
||||
|
||||
/**
|
||||
Saves the transaction changes asynchronously. For an `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.
|
||||
- parameter completion: the block executed after the save completes. Success or failure is reported by the optional `error` argument of the block.
|
||||
*/
|
||||
public func commit(_ completion: @escaping (_ result: SaveResult) -> Void) {
|
||||
public func commit(_ completion: @escaping (_ error: CoreStoreError?) -> Void) {
|
||||
|
||||
self.context.saveAsynchronouslyWithCompletion { (result) -> Void in
|
||||
self.context.saveAsynchronouslyWithCompletion { (_, error) in
|
||||
|
||||
self.result = result
|
||||
completion(result)
|
||||
completion(error)
|
||||
withExtendedLifetime(self, {})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Saves the transaction changes and waits for completion synchronously. For an `UnsafeDataTransaction`, multiple commits are allowed, although it is the developer's responsibility to ensure a reasonable leeway to prevent blocking the main thread.
|
||||
|
||||
- returns: a `SaveResult` containing the success or failure information
|
||||
- throws: a `CoreStoreError` value indicating the failure.
|
||||
*/
|
||||
public func commitAndWait() -> SaveResult {
|
||||
public func commitAndWait() throws {
|
||||
|
||||
let result = self.context.saveSynchronously(waitForMerge: true)
|
||||
self.result = result
|
||||
return result
|
||||
if case (_, let error?) = self.context.saveSynchronously(waitForMerge: true) {
|
||||
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,13 +145,4 @@ public final class UnsafeDataTransaction: BaseDataTransaction {
|
||||
|
||||
super.init(mainContext: mainContext, queue: queue, supportsUndo: supportsUndo, bypassesQueueing: true)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Obsolete
|
||||
|
||||
@available(*, obsoleted: 3.0.0, message: "Transaction contexts are now exposed through the FetchableSource and QueryableSource protocols.", renamed: "internalContext()")
|
||||
public var internalContext: NSManagedObjectContext {
|
||||
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user