Skip to content

Commit 768645b

Browse files
committed
Stricter constraints
1 parent 48a5f44 commit 768645b

4 files changed

Lines changed: 64 additions & 44 deletions

File tree

FetchRequests/Sources/Associations/FetchRequestAssociation.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ public enum AssociationReplacement<T> {
1717
/// Map an associated value's key to object
1818
public class FetchRequestAssociation<FetchedObject: FetchableObject> {
1919
/// Fetch associated values given a list of parent objects
20-
public typealias AssocationRequestByParent<AssociatedEntity> = @MainActor (_ objects: [FetchedObject], _ completion: @escaping ([FetchedObject.ID: AssociatedEntity]) -> Void) -> Void
20+
public typealias AssocationRequestByParent<AssociatedEntity> = @MainActor (_ objects: [FetchedObject], _ completion: @escaping @MainActor ([FetchedObject.ID: AssociatedEntity]) -> Void) -> Void
2121
/// Fetch associated values given a list of associated IDs
22-
public typealias AssocationRequestByID<AssociatedEntityID: Hashable, AssociatedEntity> = @MainActor (_ objects: [AssociatedEntityID], _ completion: @escaping ([AssociatedEntity]) -> Void) -> Void
22+
public typealias AssocationRequestByID<AssociatedEntityID: Hashable, AssociatedEntity> = @MainActor (_ objects: [AssociatedEntityID], _ completion: @escaping @MainActor ([AssociatedEntity]) -> Void) -> Void
2323
/// Event that represents the creation of an associated value object
2424
public typealias CreationObserved<Value, Comparison> = (Value?, Comparison) -> AssociationReplacement<Value>
2525
/// Start observing a source object

FetchRequests/Sources/Associations/FetchableEntityID.swift

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,25 @@ public protocol FetchableEntityID<FetchableEntity>: Hashable {
1616
static func fetch(byID objectID: Self) -> FetchableEntity?
1717
static func fetch(byIDs objectIDs: [Self]) -> [FetchableEntity]
1818

19-
static func fetch(byID objectID: Self, completion: @escaping (FetchableEntity?) -> Void)
20-
static func fetch(byIDs objectIDs: [Self], completion: @escaping ([FetchableEntity]) -> Void)
19+
static func fetch(
20+
byID objectID: Self,
21+
completion: @escaping @MainActor (FetchableEntity?) -> Void
22+
)
23+
static func fetch(
24+
byIDs objectIDs: [Self],
25+
completion: @escaping @MainActor ([FetchableEntity]
26+
) -> Void)
2127
}
2228

2329
extension FetchableEntityID {
2430
static func fetch(byID objectID: Self) -> FetchableEntity? {
2531
return self.fetch(byIDs: [objectID]).first
2632
}
2733

28-
static func fetch(byID objectID: Self, completion: @escaping (FetchableEntity?) -> Void) {
34+
static func fetch(
35+
byID objectID: Self,
36+
completion: @escaping @MainActor (FetchableEntity?) -> Void
37+
) {
2938
self.fetch(byIDs: [objectID]) { objects in
3039
completion(objects.first)
3140
}

FetchRequests/Tests/Models/FetchRequestAssociationTestCase.swift

Lines changed: 39 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@ extension FetchRequestAssociationTestCase {
4444
func testBasicNonOptionalAssociation() {
4545
let expected: [String: Int] = ["a": 2]
4646

47-
var calledRequest = false
47+
let expectation = XCTestExpectation(description: "calledRequest")
4848
let request: Association.AssocationRequestByParent<Int> = { [unowned self] objects, completion in
4949
XCTAssertEqual(objects, self.objects)
50-
calledRequest = true
5150
completion(expected)
51+
expectation.fulfill()
5252
}
5353

5454
let association = Association(keyPath: \.tag, request: request)
@@ -59,7 +59,7 @@ extension FetchRequestAssociationTestCase {
5959
XCTAssertEqual(results as? [String: Int], expected)
6060
}
6161

62-
XCTAssertTrue(calledRequest)
62+
wait(for: [expectation], timeout: 5)
6363

6464
// Verify we can't observe creation
6565

@@ -86,11 +86,11 @@ extension FetchRequestAssociationTestCase {
8686
func testBasicOptionalAssociation() {
8787
let expected: [String: Int] = ["a": 2]
8888

89-
var calledRequest = false
89+
let expectation = XCTestExpectation(description: "calledRequest")
9090
let request: Association.AssocationRequestByParent<Int> = { objects, completion in
9191
XCTAssertEqual(objects, self.objects)
92-
calledRequest = true
9392
completion(expected)
93+
expectation.fulfill()
9494
}
9595

9696
let association = Association(keyPath: \.tagID, request: request)
@@ -101,7 +101,7 @@ extension FetchRequestAssociationTestCase {
101101
XCTAssertEqual(results as? [String: Int], expected)
102102
}
103103

104-
XCTAssertTrue(calledRequest)
104+
wait(for: [expectation], timeout: 5)
105105

106106
// Verify we can't observe creation
107107

@@ -132,11 +132,11 @@ extension FetchRequestAssociationTestCase {
132132
func testCreatableNonOptionalAssociation() {
133133
let expected: [String: TestObject] = ["a": objects[0]]
134134

135-
var calledRequest = false
135+
let expectation = XCTestExpectation(description: "calledRequest")
136136
let request: Association.AssocationRequestByParent<TestObject> = { [unowned self] objects, completion in
137137
XCTAssertEqual(objects, self.objects)
138-
calledRequest = true
139138
completion(expected)
139+
expectation.fulfill()
140140
}
141141

142142
let token = TestObjectTestToken()
@@ -157,7 +157,7 @@ extension FetchRequestAssociationTestCase {
157157
XCTAssertEqual(results as? [String: TestObject], expected)
158158
}
159159

160-
XCTAssertTrue(calledRequest)
160+
wait(for: [expectation], timeout: 5)
161161

162162
// Verify we can observe creation
163163

@@ -195,11 +195,11 @@ extension FetchRequestAssociationTestCase {
195195
func testCreatableOptionalAssociation() {
196196
let expected: [String: TestObject] = ["a": objects[0]]
197197

198-
var calledRequest = false
198+
let expectation = XCTestExpectation(description: "calledRequest")
199199
let request: Association.AssocationRequestByParent<TestObject> = { [unowned self] objects, completion in
200200
XCTAssertEqual(objects, self.objects)
201-
calledRequest = true
202201
completion(expected)
202+
expectation.fulfill()
203203
}
204204

205205
let token = TestObjectTestToken()
@@ -220,7 +220,7 @@ extension FetchRequestAssociationTestCase {
220220
XCTAssertEqual(results as? [String: TestObject], expected)
221221
}
222222

223-
XCTAssertTrue(calledRequest)
223+
wait(for: [expectation], timeout: 5)
224224

225225
// Verify we can observe creation
226226

@@ -262,11 +262,12 @@ extension FetchRequestAssociationTestCase {
262262
func testCreatableNonOptionalAssociationByRawData() {
263263
let expected: [TestObject] = [TestObject(id: "0")]
264264

265-
var calledRequest = false
265+
let expectation = XCTestExpectation(description: "calledRequest")
266266
let request: Association.AssocationRequestByID<TestObject.ID, TestObject> = { [unowned self] objectIDs, completion in
267267
XCTAssertEqual(objectIDs, self.tagIDs)
268-
calledRequest = true
269268
completion(expected)
269+
270+
expectation.fulfill()
270271
}
271272

272273
let token = DataTestToken()
@@ -288,7 +289,7 @@ extension FetchRequestAssociationTestCase {
288289
XCTAssertEqual(results as? [String: TestObject], ["a": expected[0]])
289290
}
290291

291-
XCTAssertTrue(calledRequest)
292+
wait(for: [expectation], timeout: 5)
292293

293294
// Verify we can observe creation
294295

@@ -326,11 +327,11 @@ extension FetchRequestAssociationTestCase {
326327
func testCreatableOptionalAssociationByRawData() {
327328
let expected: [TestObject] = [TestObject(id: "0")]
328329

329-
var calledRequest = false
330+
let expectation = XCTestExpectation(description: "calledRequest")
330331
let request: Association.AssocationRequestByID<TestObject.ID, TestObject> = { [unowned self] objectIDs, completion in
331332
XCTAssertEqual(objectIDs, self.tagIDs)
332-
calledRequest = true
333333
completion(expected)
334+
expectation.fulfill()
334335
}
335336

336337
let token = DataTestToken()
@@ -352,7 +353,7 @@ extension FetchRequestAssociationTestCase {
352353
XCTAssertEqual(results as? [String: TestObject], ["a": expected[0]])
353354
}
354355

355-
XCTAssertTrue(calledRequest)
356+
wait(for: [expectation], timeout: 5)
356357

357358
// Verify we can observe creation
358359

@@ -394,11 +395,11 @@ extension FetchRequestAssociationTestCase {
394395
func testCreatableNonOptionalArrayAssociationByRawData() {
395396
let expected: [TestObject] = [TestObject(id: "0")]
396397

397-
var calledRequest = false
398+
let expectation = XCTestExpectation(description: "calledRequest")
398399
let request: Association.AssocationRequestByID<TestObject.ID, TestObject> = { [unowned self] objectIDs, completion in
399400
XCTAssertEqual(objectIDs, self.tagIDs)
400-
calledRequest = true
401401
completion(expected)
402+
expectation.fulfill()
402403
}
403404

404405
let token = DataTestToken()
@@ -424,7 +425,7 @@ extension FetchRequestAssociationTestCase {
424425
XCTAssertEqual(results as? [String: [TestObject]], ["a": expected])
425426
}
426427

427-
XCTAssertTrue(calledRequest)
428+
wait(for: [expectation], timeout: 5)
428429

429430
// Verify we can observe creation
430431

@@ -462,11 +463,12 @@ extension FetchRequestAssociationTestCase {
462463
func testCreatableOptionalArrayAssociationByRawData() {
463464
let expected: [TestObject] = [TestObject(id: "0")]
464465

465-
var calledRequest = false
466+
let expectation = XCTestExpectation(description: "calledRequest")
466467
let request: Association.AssocationRequestByID<TestObject.ID, TestObject> = { [unowned self] objectIDs, completion in
467468
XCTAssertEqual(objectIDs, self.tagIDs)
468-
calledRequest = true
469469
completion(expected)
470+
471+
expectation.fulfill()
470472
}
471473

472474
let token = DataTestToken()
@@ -492,7 +494,7 @@ extension FetchRequestAssociationTestCase {
492494
XCTAssertEqual(results as? [String: [TestObject]], ["a": expected])
493495
}
494496

495-
XCTAssertTrue(calledRequest)
497+
wait(for: [expectation], timeout: 5)
496498

497499
// Verify we can observe creation
498500

@@ -549,13 +551,13 @@ extension FetchRequestAssociationTestCase {
549551
memo[element.id] = TestObject(id: element.nonOptionalTagID)
550552
}
551553

552-
var calledRequest = false
554+
let expectation = XCTestExpectation(description: "calledRequest")
553555
association.request(objects) { results in
554-
calledRequest = true
555556
XCTAssertEqual(results as? [String: TestObject], expected)
557+
expectation.fulfill()
556558
}
557559

558-
XCTAssertTrue(calledRequest)
560+
wait(for: [expectation], timeout: 5)
559561

560562
// Verify we can observe creation
561563

@@ -608,13 +610,13 @@ extension FetchRequestAssociationTestCase {
608610
memo[element.id] = TestObject(id: element.nonOptionalTagID)
609611
}
610612

611-
var calledRequest = false
613+
let expectation = XCTestExpectation(description: "calledRequest")
612614
association.request(objects) { results in
613-
calledRequest = true
614615
XCTAssertEqual(results as? [String: TestObject], expected)
616+
expectation.fulfill()
615617
}
616618

617-
XCTAssertTrue(calledRequest)
619+
wait(for: [expectation], timeout: 5)
618620

619621
// Verify we can observe creation
620622

@@ -657,13 +659,13 @@ extension FetchRequestAssociationTestCase {
657659

658660
XCTAssertEqual(TestFetchableEntityID.fetch(byID: id), object)
659661

660-
var called = false
662+
let expectation = XCTestExpectation(description: "calledRequest")
661663
TestFetchableEntityID.fetch(byID: id) { result in
662664
XCTAssertEqual(result, object)
663-
called = true
665+
expectation.fulfill()
664666
}
665667

666-
XCTAssertTrue(called)
668+
wait(for: [expectation], timeout: 5)
667669
}
668670

669671
func testFaultByEntityID() {
@@ -770,8 +772,10 @@ private final class TestFetchableEntityID: NSObject, FetchableEntityID {
770772
return TestObject.fetch(byIDs: objectIDs.map(\.id))
771773
}
772774

773-
class func fetch(byIDs objectIDs: [TestFetchableEntityID], completion: @escaping ([TestObject]) -> Void) {
774-
completion(self.fetch(byIDs: objectIDs))
775+
class func fetch(byIDs objectIDs: [TestFetchableEntityID], completion: @escaping @MainActor ([TestObject]) -> Void) {
776+
Task {
777+
await completion(self.fetch(byIDs: objectIDs))
778+
}
775779
}
776780
}
777781

FetchRequests/Tests/TestObject+FetchableObject.swift

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,21 @@ import FetchRequests
1313

1414
extension TestObject: FetchableObjectProtocol {
1515
func observeDataChanges(_ handler: @escaping @MainActor () -> Void) -> InvalidatableToken {
16-
return self.observe(\.data, options: [.old, .new]) { @MainActor(unsafe) object, change in
16+
return self.observe(\.data, options: [.old, .new]) { object, change in
1717
guard let old = change.oldValue, let new = change.newValue, old != new else {
1818
return
1919
}
2020

21-
handler()
21+
unsafeHandler(for: handler)
2222
}
2323
}
2424

2525
func observeIsDeletedChanges(_ handler: @escaping @MainActor () -> Void) -> InvalidatableToken {
26-
return self.observe(\.isDeleted, options: [.old, .new]) { @MainActor(unsafe) object, change in
26+
return self.observe(\.isDeleted, options: [.old, .new]) { object, change in
2727
guard let old = change.oldValue, let new = change.newValue, old != new else {
2828
return
2929
}
30-
handler()
30+
unsafeHandler(for: handler)
3131
}
3232
}
3333

@@ -36,6 +36,13 @@ extension TestObject: FetchableObjectProtocol {
3636
}
3737
}
3838

39+
@MainActor(unsafe)
40+
private func unsafeHandler(for handler: @MainActor () -> Void) {
41+
assert(Thread.isMainThread)
42+
// This is a dumb wrapper, but I can't otherwise have a "clean" compile
43+
handler()
44+
}
45+
3946
// MARK: - Event Notifications
4047

4148
extension TestObject {

0 commit comments

Comments
 (0)