Skip to content

Commit 5ac5270

Browse files
authored
Merge pull request #90 from essentialdevelopercom/swift-concurrency
Migrate Stores to async/await
2 parents 67ceace + 624984e commit 5ac5270

File tree

6 files changed

+67
-66
lines changed

6 files changed

+67
-66
lines changed

EssentialApp/EssentialApp/CombineHelpers.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ extension AnyDispatchQueueScheduler {
227227
CoreDataFeedStoreScheduler(store: store).eraseToAnyScheduler()
228228
}
229229

230+
@MainActor
230231
private struct CoreDataFeedStoreScheduler: Scheduler {
231232
let store: CoreDataFeedStore
232233

@@ -239,7 +240,9 @@ extension AnyDispatchQueueScheduler {
239240
action()
240241
} else {
241242
nonisolated(unsafe) let uncheckedAction = action
242-
store.perform { uncheckedAction() }
243+
Task.immediate {
244+
await store.perform { uncheckedAction() }
245+
}
243246
}
244247
return AnyCancellable {}
245248
}
@@ -249,7 +252,9 @@ extension AnyDispatchQueueScheduler {
249252
action()
250253
} else {
251254
nonisolated(unsafe) let uncheckedAction = action
252-
store.perform { uncheckedAction() }
255+
Task.immediate {
256+
await store.perform { uncheckedAction() }
257+
}
253258
}
254259
}
255260

@@ -258,7 +263,9 @@ extension AnyDispatchQueueScheduler {
258263
action()
259264
} else {
260265
nonisolated(unsafe) let uncheckedAction = action
261-
store.perform { uncheckedAction() }
266+
Task.immediate {
267+
await store.perform { uncheckedAction() }
268+
}
262269
}
263270
}
264271
}

EssentialFeed/EssentialFeed/Feed Cache/Infrastructure/CoreData/CoreDataFeedStore.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ public final class CoreDataFeedStore: Sendable {
4545
}
4646
}
4747

48-
public func perform(_ action: @Sendable @escaping () -> Void) {
49-
context.perform(action)
48+
public func perform<T>(_ action: @escaping @Sendable () throws -> T) async rethrows -> T {
49+
try await context.perform(action)
5050
}
5151

5252
private func cleanUpReferencesToPersistentStores() {

EssentialFeed/EssentialFeedTests/Feed Cache/CoreDataFeedImageDataStoreTests.swift

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,45 +8,42 @@ import EssentialFeed
88
@MainActor
99
class CoreDataFeedImageDataStoreTests: XCTestCase, FeedImageDataStoreSpecs {
1010

11-
func test_retrieveImageData_deliversNotFoundWhenEmpty() throws {
12-
try makeSUT { sut, imageDataURL in
11+
func test_retrieveImageData_deliversNotFoundWhenEmpty() async throws {
12+
try await makeSUT { sut, imageDataURL in
1313
assertThatRetrieveImageDataDeliversNotFoundOnEmptyCache(on: sut, imageDataURL: imageDataURL)
1414
}
1515
}
1616

17-
func test_retrieveImageData_deliversNotFoundWhenStoredDataURLDoesNotMatch() throws {
18-
try makeSUT { sut, imageDataURL in
17+
func test_retrieveImageData_deliversNotFoundWhenStoredDataURLDoesNotMatch() async throws {
18+
try await makeSUT { sut, imageDataURL in
1919
assertThatRetrieveImageDataDeliversNotFoundWhenStoredDataURLDoesNotMatch(on: sut, imageDataURL: imageDataURL)
2020
}
2121
}
2222

23-
func test_retrieveImageData_deliversFoundDataWhenThereIsAStoredImageDataMatchingURL() throws {
24-
try makeSUT { sut, imageDataURL in
23+
func test_retrieveImageData_deliversFoundDataWhenThereIsAStoredImageDataMatchingURL() async throws {
24+
try await makeSUT { sut, imageDataURL in
2525
assertThatRetrieveImageDataDeliversFoundDataWhenThereIsAStoredImageDataMatchingURL(on: sut, imageDataURL: imageDataURL)
2626
}
2727
}
2828

29-
func test_retrieveImageData_deliversLastInsertedValue() throws {
30-
try makeSUT { sut, imageDataURL in
29+
func test_retrieveImageData_deliversLastInsertedValue() async throws {
30+
try await makeSUT { sut, imageDataURL in
3131
assertThatRetrieveImageDataDeliversLastInsertedValueForURL(on: sut, imageDataURL: imageDataURL)
3232
}
3333
}
3434

3535
// - MARK: Helpers
3636

37-
private func makeSUT(_ test: @Sendable @escaping (CoreDataFeedStore, URL) -> Void, file: StaticString = #filePath, line: UInt = #line) throws {
37+
private func makeSUT(_ test: @Sendable @escaping (CoreDataFeedStore, URL) -> Void, file: StaticString = #filePath, line: UInt = #line) async throws {
3838
let storeURL = URL(fileURLWithPath: "/dev/null")
3939
let sut = try CoreDataFeedStore(storeURL: storeURL)
4040
trackForMemoryLeaks(sut, file: file, line: line)
4141

42-
let exp = expectation(description: "wait for operation")
43-
sut.perform {
42+
await sut.perform {
4443
let imageDataURL = URL(string: "http://a-url.com")!
4544
insertFeedImage(with: imageDataURL, into: sut, file: file, line: line)
4645
test(sut, imageDataURL)
47-
exp.fulfill()
4846
}
49-
wait(for: [exp], timeout: 0.1)
5047
}
5148

5249
}

EssentialFeed/EssentialFeedTests/Feed Cache/CoreDataFeedStoreTests.swift

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,85 +8,82 @@ import EssentialFeed
88
@MainActor
99
class CoreDataFeedStoreTests: XCTestCase, FeedStoreSpecs {
1010

11-
func test_retrieve_deliversEmptyOnEmptyCache() throws {
12-
try makeSUT { sut in
11+
func test_retrieve_deliversEmptyOnEmptyCache() async throws {
12+
try await makeSUT { sut in
1313
assertThatRetrieveDeliversEmptyOnEmptyCache(on: sut)
1414
}
1515
}
1616

17-
func test_retrieve_hasNoSideEffectsOnEmptyCache() throws {
18-
try makeSUT { sut in
17+
func test_retrieve_hasNoSideEffectsOnEmptyCache() async throws {
18+
try await makeSUT { sut in
1919
assertThatRetrieveHasNoSideEffectsOnEmptyCache(on: sut)
2020
}
2121
}
2222

23-
func test_retrieve_deliversFoundValuesOnNonEmptyCache() throws {
24-
try makeSUT { sut in
23+
func test_retrieve_deliversFoundValuesOnNonEmptyCache() async throws {
24+
try await makeSUT { sut in
2525
assertThatRetrieveDeliversFoundValuesOnNonEmptyCache(on: sut)
2626
}
2727
}
2828

29-
func test_retrieve_hasNoSideEffectsOnNonEmptyCache() throws {
30-
try makeSUT { sut in
29+
func test_retrieve_hasNoSideEffectsOnNonEmptyCache() async throws {
30+
try await makeSUT { sut in
3131
assertThatRetrieveHasNoSideEffectsOnNonEmptyCache(on: sut)
3232
}
3333
}
3434

35-
func test_insert_deliversNoErrorOnEmptyCache() throws {
36-
try makeSUT { sut in
35+
func test_insert_deliversNoErrorOnEmptyCache() async throws {
36+
try await makeSUT { sut in
3737
assertThatInsertDeliversNoErrorOnEmptyCache(on: sut)
3838
}
3939
}
4040

41-
func test_insert_deliversNoErrorOnNonEmptyCache() throws {
42-
try makeSUT { sut in
41+
func test_insert_deliversNoErrorOnNonEmptyCache() async throws {
42+
try await makeSUT { sut in
4343
assertThatInsertDeliversNoErrorOnNonEmptyCache(on: sut)
4444
}
4545
}
4646

47-
func test_insert_overridesPreviouslyInsertedCacheValues() throws {
48-
try makeSUT { sut in
47+
func test_insert_overridesPreviouslyInsertedCacheValues() async throws {
48+
try await makeSUT { sut in
4949
assertThatInsertOverridesPreviouslyInsertedCacheValues(on: sut)
5050
}
5151
}
5252

53-
func test_delete_deliversNoErrorOnEmptyCache() throws {
54-
try makeSUT { sut in
53+
func test_delete_deliversNoErrorOnEmptyCache() async throws {
54+
try await makeSUT { sut in
5555
assertThatDeleteDeliversNoErrorOnEmptyCache(on: sut)
5656
}
5757
}
5858

59-
func test_delete_hasNoSideEffectsOnEmptyCache() throws {
60-
try makeSUT { sut in
59+
func test_delete_hasNoSideEffectsOnEmptyCache() async throws {
60+
try await makeSUT { sut in
6161
assertThatDeleteHasNoSideEffectsOnEmptyCache(on: sut)
6262
}
6363
}
6464

65-
func test_delete_deliversNoErrorOnNonEmptyCache() throws {
66-
try makeSUT { sut in
65+
func test_delete_deliversNoErrorOnNonEmptyCache() async throws {
66+
try await makeSUT { sut in
6767
assertThatDeleteDeliversNoErrorOnNonEmptyCache(on: sut)
6868
}
6969
}
7070

71-
func test_delete_emptiesPreviouslyInsertedCache() throws {
72-
try makeSUT { sut in
71+
func test_delete_emptiesPreviouslyInsertedCache() async throws {
72+
try await makeSUT { sut in
7373
assertThatDeleteEmptiesPreviouslyInsertedCache(on: sut)
7474
}
7575
}
7676

7777
// - MARK: Helpers
7878

79-
private func makeSUT(_ test: @Sendable @escaping (CoreDataFeedStore) -> Void, file: StaticString = #filePath, line: UInt = #line) throws {
79+
private func makeSUT(_ test: @Sendable @escaping (CoreDataFeedStore) -> Void, file: StaticString = #filePath, line: UInt = #line) async throws {
8080
let storeURL = URL(fileURLWithPath: "/dev/null")
8181
let sut = try CoreDataFeedStore(storeURL: storeURL)
8282
trackForMemoryLeaks(sut, file: file, line: line)
8383

84-
let exp = expectation(description: "wait for operation")
85-
sut.perform {
84+
await sut.perform {
8685
test(sut)
87-
exp.fulfill()
8886
}
89-
wait(for: [exp], timeout: 0.1)
9087
}
9188

9289
}

EssentialFeed/EssentialFeedTests/Feed Cache/FeedImageDataStoreSpecs/FeedImageDataStoreSpecs.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
import Foundation
66

77
protocol FeedImageDataStoreSpecs {
8-
func test_retrieveImageData_deliversNotFoundWhenEmpty() throws
9-
func test_retrieveImageData_deliversNotFoundWhenStoredDataURLDoesNotMatch() throws
10-
func test_retrieveImageData_deliversFoundDataWhenThereIsAStoredImageDataMatchingURL() throws
11-
func test_retrieveImageData_deliversLastInsertedValue() throws
8+
func test_retrieveImageData_deliversNotFoundWhenEmpty() async throws
9+
func test_retrieveImageData_deliversNotFoundWhenStoredDataURLDoesNotMatch() async throws
10+
func test_retrieveImageData_deliversFoundDataWhenThereIsAStoredImageDataMatchingURL() async throws
11+
func test_retrieveImageData_deliversLastInsertedValue() async throws
1212
}

EssentialFeed/EssentialFeedTests/Feed Cache/FeedStoreSpecs/FeedStoreSpecs.swift

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,34 @@
55
import Foundation
66

77
protocol FeedStoreSpecs {
8-
func test_retrieve_deliversEmptyOnEmptyCache() throws
9-
func test_retrieve_hasNoSideEffectsOnEmptyCache() throws
10-
func test_retrieve_deliversFoundValuesOnNonEmptyCache() throws
11-
func test_retrieve_hasNoSideEffectsOnNonEmptyCache() throws
8+
func test_retrieve_deliversEmptyOnEmptyCache() async throws
9+
func test_retrieve_hasNoSideEffectsOnEmptyCache() async throws
10+
func test_retrieve_deliversFoundValuesOnNonEmptyCache() async throws
11+
func test_retrieve_hasNoSideEffectsOnNonEmptyCache() async throws
1212

13-
func test_insert_deliversNoErrorOnEmptyCache() throws
14-
func test_insert_deliversNoErrorOnNonEmptyCache() throws
15-
func test_insert_overridesPreviouslyInsertedCacheValues() throws
13+
func test_insert_deliversNoErrorOnEmptyCache() async throws
14+
func test_insert_deliversNoErrorOnNonEmptyCache() async throws
15+
func test_insert_overridesPreviouslyInsertedCacheValues() async throws
1616

17-
func test_delete_deliversNoErrorOnEmptyCache() throws
18-
func test_delete_hasNoSideEffectsOnEmptyCache() throws
19-
func test_delete_deliversNoErrorOnNonEmptyCache() throws
20-
func test_delete_emptiesPreviouslyInsertedCache() throws
17+
func test_delete_deliversNoErrorOnEmptyCache() async throws
18+
func test_delete_hasNoSideEffectsOnEmptyCache() async throws
19+
func test_delete_deliversNoErrorOnNonEmptyCache() async throws
20+
func test_delete_emptiesPreviouslyInsertedCache() async throws
2121
}
2222

2323
protocol FailableRetrieveFeedStoreSpecs: FeedStoreSpecs {
24-
func test_retrieve_deliversFailureOnRetrievalError() throws
25-
func test_retrieve_hasNoSideEffectsOnFailure() throws
24+
func test_retrieve_deliversFailureOnRetrievalError() async throws
25+
func test_retrieve_hasNoSideEffectsOnFailure() async throws
2626
}
2727

2828
protocol FailableInsertFeedStoreSpecs: FeedStoreSpecs {
29-
func test_insert_deliversErrorOnInsertionError() throws
30-
func test_insert_hasNoSideEffectsOnInsertionError() throws
29+
func test_insert_deliversErrorOnInsertionError() async throws
30+
func test_insert_hasNoSideEffectsOnInsertionError() async throws
3131
}
3232

3333
protocol FailableDeleteFeedStoreSpecs: FeedStoreSpecs {
34-
func test_delete_deliversErrorOnDeletionError() throws
35-
func test_delete_hasNoSideEffectsOnDeletionError() throws
34+
func test_delete_deliversErrorOnDeletionError() async throws
35+
func test_delete_hasNoSideEffectsOnDeletionError() async throws
3636
}
3737

3838
typealias FailableFeedStoreSpecs = FailableRetrieveFeedStoreSpecs & FailableInsertFeedStoreSpecs & FailableDeleteFeedStoreSpecs

0 commit comments

Comments
 (0)