From f4e123e5da1468f49df400c04425743ee0b62f8e Mon Sep 17 00:00:00 2001 From: SeungYeop Yeom Date: Sat, 7 Sep 2024 16:54:58 +0900 Subject: [PATCH] Add `Sendable` to key path literals --- .../AsyncViewStateSequence.swift | 13 ++++++++++ .../TestHelper/Publisher+Async.swift | 2 +- Tests/OneWayTests/ViewStoreTests.swift | 26 +++++++------------ 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/Sources/OneWay/AsyncSequences/AsyncViewStateSequence.swift b/Sources/OneWay/AsyncSequences/AsyncViewStateSequence.swift index 6b8879d..5040fbc 100644 --- a/Sources/OneWay/AsyncSequences/AsyncViewStateSequence.swift +++ b/Sources/OneWay/AsyncSequences/AsyncViewStateSequence.swift @@ -63,6 +63,18 @@ where State: Sendable & Equatable { /// /// - Parameter dynamicMember: a key path for the original state. /// - Returns: A new stream that has a part of the original state. + #if swift(>=6) + public subscript( + dynamicMember keyPath: KeyPath & Sendable + ) -> AsyncMapSequence, Property> { + let (stream, continuation) = AsyncStream.makeStream() + continuations.append(continuation) + if let last { + continuation.yield(last) + } + return stream.map { $0[keyPath: keyPath] } + } + #else public subscript( dynamicMember keyPath: KeyPath ) -> AsyncMapSequence, Property> { @@ -73,6 +85,7 @@ where State: Sendable & Equatable { } return stream.map { $0[keyPath: keyPath] } } + #endif } extension AsyncViewStateSequence: Sendable where State: Sendable { } diff --git a/Tests/OneWayTests/TestHelper/Publisher+Async.swift b/Tests/OneWayTests/TestHelper/Publisher+Async.swift index 03c4d34..9c6993f 100644 --- a/Tests/OneWayTests/TestHelper/Publisher+Async.swift +++ b/Tests/OneWayTests/TestHelper/Publisher+Async.swift @@ -8,7 +8,7 @@ #if canImport(Combine) @preconcurrency import Combine -extension Publisher where Failure == Never { +extension Publisher where Failure == Never, Output: Sendable { public var stream: AsyncStream { AsyncStream { continuation in let cancellable = self.sink { completion in diff --git a/Tests/OneWayTests/ViewStoreTests.swift b/Tests/OneWayTests/ViewStoreTests.swift index 834b2cb..9df1be3 100644 --- a/Tests/OneWayTests/ViewStoreTests.swift +++ b/Tests/OneWayTests/ViewStoreTests.swift @@ -14,8 +14,7 @@ final class ViewStoreTests: XCTestCase { private var sut: ViewStore! @MainActor - override func setUp() { - super.setUp() + override func setUp() async throws { sut = ViewStore( reducer: TestReducer(), state: TestReducer.State(count: 0) @@ -23,8 +22,7 @@ final class ViewStoreTests: XCTestCase { } @MainActor - override func tearDown() { - super.tearDown() + override func tearDown() async throws { sut = nil } @@ -40,17 +38,15 @@ final class ViewStoreTests: XCTestCase { } } -#if swift(>=5.10) @MainActor func test_sendSeveralActions() async { sut.send(.increment) sut.send(.increment) sut.send(.twice) - nonisolated(unsafe) let sut = sut! + let sut = sut! await sendableExpectWithMainActor { await sut.state.count == 4 } } -#endif @MainActor func test_triggeredState() async { @@ -132,27 +128,26 @@ final class ViewStoreTests: XCTestCase { XCTAssertEqual(result, [0, 1, 2, 3, 4]) } -#if swift(>=5.10) @MainActor func test_asyncViewStateSequenceForMultipleConsumers() async { let expectation = expectation(description: #function) - nonisolated(unsafe) let sut = sut! + let sut = sut! let result = TestResult(expectation, expectedCount: 15) Task { @MainActor in await withTaskGroup(of: Void.self) { group in - group.addTask { @MainActor in - for await state in sut.states { + group.addTask { + for await state in await sut.states { await result.insert(state.count) } } - group.addTask { @MainActor in - for await count in sut.states.count { + group.addTask { + for await count in await sut.states.count { await result.insert(count) } } - group.addTask { @MainActor in - for await count in sut.states.count { + group.addTask { + for await count in await sut.states.count { await result.insert(count) } } @@ -176,7 +171,6 @@ final class ViewStoreTests: XCTestCase { ] ) } -#endif } private struct TestReducer: Reducer {