Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sendable miscellany: effects, publishers, etc. #3317

Merged
merged 38 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
709eaf8
`@preconcurrency @MainActor` isolation of `Store`
stephencelis Aug 10, 2024
9add3a1
Remove unneeded `@MainActor`s
stephencelis Aug 10, 2024
1b053fe
Remove thread checking code
stephencelis Aug 10, 2024
fcd9c8c
Remove unneeded `@MainActor`s
stephencelis Aug 10, 2024
d3440f3
Swift 5.10 compatibility fixes
stephencelis Aug 10, 2024
67772a2
wip
stephencelis Aug 10, 2024
1ab386d
More 5.10 fixes
stephencelis Aug 10, 2024
1e23bca
wip
mbrandonw Aug 10, 2024
fe7bd98
fixes
mbrandonw Aug 10, 2024
9972512
wip
stephencelis Aug 11, 2024
f2d710b
wip
stephencelis Aug 11, 2024
2033a26
up the timeout
mbrandonw Aug 11, 2024
24312e5
wip
stephencelis Aug 12, 2024
dab61ef
Merge remote-tracking branch 'origin/main' into main-actor-preconcurr…
stephencelis Aug 12, 2024
73e12ca
Merge remote-tracking branch 'origin/main' into main-actor-preconcurr…
stephencelis Aug 13, 2024
02622e5
Fixes
stephencelis Aug 13, 2024
a9e8ebd
wip
stephencelis Aug 12, 2024
1cad10b
wip
stephencelis Aug 12, 2024
cd3a014
wip
stephencelis Aug 13, 2024
b25b608
wip
stephencelis Aug 27, 2024
f487678
Merge remote-tracking branch 'origin/main' into key-path-sendability
stephencelis Aug 27, 2024
273fd57
wip
stephencelis Aug 27, 2024
aefc73a
Merge branch 'main' into key-path-sendability
stephencelis Aug 28, 2024
7fd9436
Fix binding action sendability
stephencelis Aug 28, 2024
a4a00e4
Address more binding action sendability
stephencelis Aug 28, 2024
b85004b
more bindable action sendability
stephencelis Aug 28, 2024
c07b02d
more bindable action warnings
stephencelis Aug 28, 2024
de6b901
fix
stephencelis Aug 28, 2024
a25b469
Make `Effect.map` sendable
stephencelis Aug 28, 2024
5268ca1
Make `Effect.actions` sendable
stephencelis Aug 28, 2024
bafeab2
Make `AnyPublisher.create` sendable
stephencelis Aug 28, 2024
6234b4f
Make `_SynthesizedConformance` sendable
stephencelis Aug 28, 2024
ec4183f
Avoid non-sendable captures of `self` in reducers
stephencelis Aug 28, 2024
7eae6be
Make `ViewStore.yield` sendable
stephencelis Aug 28, 2024
a25ed28
Address internal sendability warning
stephencelis Aug 28, 2024
2faac88
fix
stephencelis Aug 28, 2024
1606c50
Another small warning
stephencelis Aug 28, 2024
6e5fa33
Merge branch 'main' into sendable-misc
stephencelis Aug 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
wip
  • Loading branch information
stephencelis committed Aug 27, 2024
commit b25b608cfbf8e73a95135989f42171402cb8a2c1
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ final class RecursionTests: XCTestCase {
$0.rows.append(Nested.State(id: UUID(0)))
}

await store.send(\.rows[id:UUID(0)].addRowButtonTapped) {
await store.send(\.rows[id: UUID(0)].addRowButtonTapped) {
$0.rows[id: UUID(0)]?.rows.append(Nested.State(id: UUID(1)))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,20 @@ final class ReusableComponentsFavoritingTests: XCTestCase {
)
}

await store.send(\.episodes[id:UUID(0)].favorite.buttonTapped) {
await store.send(\.episodes[id: UUID(0)].favorite.buttonTapped) {
$0.episodes[id: UUID(0)]?.isFavorite = true
}
await clock.advance(by: .seconds(1))
await store.receive(\.episodes[id:episodes[0].id].favorite.response.success)
await store.receive(\.episodes[id: episodes[0].id].favorite.response.success)

await store.send(\.episodes[id:episodes[1].id].favorite.buttonTapped) {
await store.send(\.episodes[id: episodes[1].id].favorite.buttonTapped) {
$0.episodes[id: UUID(1)]?.isFavorite = true
}
await store.send(\.episodes[id:episodes[1].id].favorite.buttonTapped) {
await store.send(\.episodes[id: episodes[1].id].favorite.buttonTapped) {
$0.episodes[id: UUID(1)]?.isFavorite = false
}
await clock.advance(by: .seconds(1))
await store.receive(\.episodes[id:episodes[1].id].favorite.response.success)
await store.receive(\.episodes[id: episodes[1].id].favorite.response.success)
}

func testUnhappyPath() async {
Expand All @@ -61,17 +61,17 @@ final class ReusableComponentsFavoritingTests: XCTestCase {
Episodes(favorite: { _, _ in throw FavoriteError() })
}

await store.send(\.episodes[id:UUID(0)].favorite.buttonTapped) {
await store.send(\.episodes[id: UUID(0)].favorite.buttonTapped) {
$0.episodes[id: UUID(0)]?.isFavorite = true
}

await store.receive(\.episodes[id:episodes[0].id].favorite.response.failure) {
await store.receive(\.episodes[id: episodes[0].id].favorite.response.failure) {
$0.episodes[id: UUID(0)]?.alert = AlertState {
TextState("Favoriting failed.")
}
}

await store.send(\.episodes[id:UUID(0)].favorite.alert.dismiss) {
await store.send(\.episodes[id: UUID(0)].favorite.alert.dismiss) {
$0.episodes[id: UUID(0)]?.alert = nil
$0.episodes[id: UUID(0)]?.isFavorite = false
}
Expand Down
2 changes: 1 addition & 1 deletion Examples/CaseStudies/UIKitCaseStudies/ListsOfState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ final class CountersTableViewController: UITableViewController {

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let id = store.counters[indexPath.row].id
if let store = store.scope(state: \.counters[id:id], action: \.counters[id:id]) {
if let store = store.scope(state: \.counters[id: id], action: \.counters[id: id]) {
navigationController?.pushViewController(CounterViewController(store: store), animated: true)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,24 @@ final class UIKitCaseStudiesTests: XCTestCase {
CounterList()
}

await store.send(\.counters[id:firstState.id].incrementButtonTapped) {
await store.send(\.counters[id: firstState.id].incrementButtonTapped) {
$0.counters[id: firstState.id]?.count = 1
}
await store.send(\.counters[id:firstState.id].decrementButtonTapped) {
await store.send(\.counters[id: firstState.id].decrementButtonTapped) {
$0.counters[id: firstState.id]?.count = 0
}

await store.send(\.counters[id:secondState.id].incrementButtonTapped) {
await store.send(\.counters[id: secondState.id].incrementButtonTapped) {
$0.counters[id: secondState.id]?.count = 1
}
await store.send(\.counters[id:secondState.id].decrementButtonTapped) {
await store.send(\.counters[id: secondState.id].decrementButtonTapped) {
$0.counters[id: secondState.id]?.count = 0
}

await store.send(\.counters[id:thirdState.id].incrementButtonTapped) {
await store.send(\.counters[id: thirdState.id].incrementButtonTapped) {
$0.counters[id: thirdState.id]?.count = 1
}
await store.send(\.counters[id:thirdState.id].decrementButtonTapped) {
await store.send(\.counters[id: thirdState.id].decrementButtonTapped) {
$0.counters[id: thirdState.id]?.count = 0
}
}
Expand Down
7 changes: 4 additions & 3 deletions Examples/SyncUps/SyncUps/Dependencies/SpeechRecognizer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ struct SpeechClient {
.denied
}
var startTask:
@Sendable (_ request: UncheckedSendable<SFSpeechAudioBufferRecognitionRequest>) async -> AsyncThrowingStream<
SpeechRecognitionResult, Error
> = { _ in .finished() }
@Sendable (_ request: UncheckedSendable<SFSpeechAudioBufferRecognitionRequest>) async ->
AsyncThrowingStream<
SpeechRecognitionResult, Error
> = { _ in .finished() }
}

extension SpeechClient: DependencyKey {
Expand Down
12 changes: 6 additions & 6 deletions Examples/SyncUps/SyncUpsTests/AppFeatureTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,18 @@ final class AppFeatureTests: XCTestCase {
$0.path[id: 0] = .detail(SyncUpDetail.State(syncUp: sharedSyncUp))
}

await store.send(\.path[id:0].detail.editButtonTapped) {
await store.send(\.path[id: 0].detail.editButtonTapped) {
$0.path[id: 0]?.modify(\.detail) { $0.destination = .edit(SyncUpForm.State(syncUp: syncUp)) }
}

syncUp.title = "Blob"
await store.send(\.path[id:0].detail.destination.edit.binding.syncUp, syncUp) {
await store.send(\.path[id: 0].detail.destination.edit.binding.syncUp, syncUp) {
$0.path[id: 0]?.modify(\.detail) {
$0.destination?.modify(\.edit) { $0.syncUp.title = "Blob" }
}
}

await store.send(\.path[id:0].detail.doneEditingButtonTapped) {
await store.send(\.path[id: 0].detail.doneEditingButtonTapped) {
$0.path[id: 0]?.modify(\.detail) {
$0.destination = nil
$0.syncUp.title = "Blob"
Expand All @@ -50,11 +50,11 @@ final class AppFeatureTests: XCTestCase {
$0.path[id: 0] = .detail(SyncUpDetail.State(syncUp: sharedSyncUp))
}

await store.send(\.path[id:0].detail.deleteButtonTapped) {
await store.send(\.path[id: 0].detail.deleteButtonTapped) {
$0.path[id: 0]?.modify(\.detail) { $0.destination = .alert(.deleteSyncUp) }
}

await store.send(\.path[id:0].detail.destination.alert.confirmDeletion) {
await store.send(\.path[id: 0].detail.destination.alert.confirmDeletion) {
$0.path[id: 0]?.modify(\.detail) { $0.destination = nil }
$0.syncUpsList.syncUps = []
}
Expand Down Expand Up @@ -104,7 +104,7 @@ final class AppFeatureTests: XCTestCase {
}

await store.withExhaustivity(.off) {
await store.send(\.path[id:1].record.onTask)
await store.send(\.path[id: 1].record.onTask)
await store.receive(\.path.popFrom) {
XCTAssertEqual($0.path.count, 1)
}
Expand Down
10 changes: 5 additions & 5 deletions Examples/Todos/TodosTests/TodosTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ final class TodosTests: XCTestCase {
Todos()
}

await store.send(\.todos[id:UUID(0)].binding.description, "Learn Composable Architecture") {
await store.send(\.todos[id: UUID(0)].binding.description, "Learn Composable Architecture") {
$0.todos[id: UUID(0)]?.description = "Learn Composable Architecture"
}
}
Expand All @@ -82,7 +82,7 @@ final class TodosTests: XCTestCase {
$0.continuousClock = clock
}

await store.send(\.todos[id:UUID(0)].binding.isComplete, true) {
await store.send(\.todos[id: UUID(0)].binding.isComplete, true) {
$0.todos[id: UUID(0)]?.isComplete = true
}
await clock.advance(by: .seconds(1))
Expand Down Expand Up @@ -116,11 +116,11 @@ final class TodosTests: XCTestCase {
$0.continuousClock = clock
}

await store.send(\.todos[id:UUID(0)].binding.isComplete, true) {
await store.send(\.todos[id: UUID(0)].binding.isComplete, true) {
$0.todos[id: UUID(0)]?.isComplete = true
}
await clock.advance(by: .milliseconds(500))
await store.send(\.todos[id:UUID(0)].binding.isComplete, false) {
await store.send(\.todos[id: UUID(0)].binding.isComplete, false) {
$0.todos[id: UUID(0)]?.isComplete = false
}
await clock.advance(by: .seconds(1))
Expand Down Expand Up @@ -336,7 +336,7 @@ final class TodosTests: XCTestCase {
await store.send(\.binding.filter, .completed) {
$0.filter = .completed
}
await store.send(\.todos[id:UUID(1)].binding.description, "Did this already") {
await store.send(\.todos[id: UUID(1)].binding.description, "Did this already") {
$0.todos[id: UUID(1)]?.description = "Did this already"
}
}
Expand Down
38 changes: 19 additions & 19 deletions Examples/VoiceMemos/VoiceMemosTests/VoiceMemosTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,25 +61,25 @@ final class VoiceMemosTests: XCTestCase {
)
]
}
await store.send(\.voiceMemos[id:deadbeefURL].playButtonTapped) {
await store.send(\.voiceMemos[id: deadbeefURL].playButtonTapped) {
$0.voiceMemos[id: deadbeefURL]?.mode = .playing(progress: 0)
}
await store.receive(\.voiceMemos[id:deadbeefURL].delegate.playbackStarted)
await store.receive(\.voiceMemos[id: deadbeefURL].delegate.playbackStarted)
await clock.run()

await store.receive(\.voiceMemos[id:deadbeefURL].timerUpdated) {
await store.receive(\.voiceMemos[id: deadbeefURL].timerUpdated) {
$0.voiceMemos[id: deadbeefURL]?.mode = .playing(progress: 0.2)
}
await store.receive(\.voiceMemos[id:deadbeefURL].timerUpdated) {
await store.receive(\.voiceMemos[id: deadbeefURL].timerUpdated) {
$0.voiceMemos[id: deadbeefURL]?.mode = .playing(progress: 0.4)
}
await store.receive(\.voiceMemos[id:deadbeefURL].timerUpdated) {
await store.receive(\.voiceMemos[id: deadbeefURL].timerUpdated) {
$0.voiceMemos[id: deadbeefURL]?.mode = .playing(progress: 0.6)
}
await store.receive(\.voiceMemos[id:deadbeefURL].timerUpdated) {
await store.receive(\.voiceMemos[id: deadbeefURL].timerUpdated) {
$0.voiceMemos[id: deadbeefURL]?.mode = .playing(progress: 0.8)
}
await store.receive(\.voiceMemos[id:deadbeefURL].audioPlayerClient.success) {
await store.receive(\.voiceMemos[id: deadbeefURL].audioPlayerClient.success) {
$0.voiceMemos[id: deadbeefURL]?.mode = .notPlaying
}
}
Expand Down Expand Up @@ -260,20 +260,20 @@ final class VoiceMemosTests: XCTestCase {
$0.continuousClock = clock
}

await store.send(\.voiceMemos[id:url].playButtonTapped) {
await store.send(\.voiceMemos[id: url].playButtonTapped) {
$0.voiceMemos[id: url]?.mode = .playing(progress: 0)
}
await store.receive(\.voiceMemos[id:url].delegate.playbackStarted)
await store.receive(\.voiceMemos[id: url].delegate.playbackStarted)
await clock.advance(by: .milliseconds(500))
await store.receive(\.voiceMemos[id:url].timerUpdated) {
await store.receive(\.voiceMemos[id: url].timerUpdated) {
$0.voiceMemos[id: url]?.mode = .playing(progress: 0.4)
}
await clock.advance(by: .milliseconds(500))
await store.receive(\.voiceMemos[id:url].timerUpdated) {
await store.receive(\.voiceMemos[id: url].timerUpdated) {
$0.voiceMemos[id: url]?.mode = .playing(progress: 0.8)
}
await clock.advance(by: .milliseconds(250))
await store.receive(\.voiceMemos[id:url].audioPlayerClient.success) {
await store.receive(\.voiceMemos[id: url].audioPlayerClient.success) {
$0.voiceMemos[id: url]?.mode = .notPlaying
}
}
Expand Down Expand Up @@ -302,14 +302,14 @@ final class VoiceMemosTests: XCTestCase {
$0.continuousClock = clock
}

let task = await store.send(\.voiceMemos[id:url].playButtonTapped) {
let task = await store.send(\.voiceMemos[id: url].playButtonTapped) {
$0.voiceMemos[id: url]?.mode = .playing(progress: 0)
}
await store.receive(\.voiceMemos[id:url].delegate.playbackStarted)
await store.receive(\.voiceMemos[id:url].audioPlayerClient.failure) {
await store.receive(\.voiceMemos[id: url].delegate.playbackStarted)
await store.receive(\.voiceMemos[id: url].audioPlayerClient.failure) {
$0.voiceMemos[id: url]?.mode = .notPlaying
}
await store.receive(\.voiceMemos[id:url].delegate.playbackFailed) {
await store.receive(\.voiceMemos[id: url].delegate.playbackFailed) {
$0.alert = AlertState { TextState("Voice memo playback failed.") }
}
await task.cancel()
Expand All @@ -333,7 +333,7 @@ final class VoiceMemosTests: XCTestCase {
VoiceMemos()
}

await store.send(\.voiceMemos[id:url].playButtonTapped) {
await store.send(\.voiceMemos[id: url].playButtonTapped) {
$0.voiceMemos[id: url]?.mode = .notPlaying
}
}
Expand Down Expand Up @@ -435,10 +435,10 @@ final class VoiceMemosTests: XCTestCase {
$0.continuousClock = clock
}

await store.send(\.voiceMemos[id:url].playButtonTapped) {
await store.send(\.voiceMemos[id: url].playButtonTapped) {
$0.voiceMemos[id: url]?.mode = .playing(progress: 0)
}
await store.receive(\.voiceMemos[id:url].delegate.playbackStarted)
await store.receive(\.voiceMemos[id: url].delegate.playbackStarted)
await store.send(.onDelete([0])) {
$0.voiceMemos = []
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/ComposableArchitecture/CaseReducer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ where State: CaseReducerState, Body: Reducer, Body.State == State, Body.Action =
#if swift(<5.10)
@MainActor(unsafe)
#else
@preconcurrency @MainActor
@preconcurrency@MainActor
#endif
static func scope(_ store: Store<State, Action>) -> CaseScope
}
Expand Down
6 changes: 3 additions & 3 deletions Sources/ComposableArchitecture/Internal/EphemeralState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
/// with they are dismissed. Such features do not manage any behavior on the inside.
///
/// Alerts and confirmation dialogs are examples of this kind of state.
@_documentation(visibility:public)
@_documentation(visibility: public)
public protocol _EphemeralState<Action> {
associatedtype Action
static var actionType: Any.Type { get }
Expand All @@ -14,10 +14,10 @@ extension _EphemeralState {
public static var actionType: Any.Type { Action.self }
}

@_documentation(visibility:private)
@_documentation(visibility: private)
extension AlertState: _EphemeralState {}

@_documentation(visibility:private)
@_documentation(visibility: private)
@available(iOS 13, macOS 12, tvOS 13, watchOS 6, *)
extension ConfirmationDialogState: _EphemeralState {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Foundation
import IssueReporting

extension Notification.Name {
@_documentation(visibility:private)
@_documentation(visibility: private)
@available(*, deprecated, renamed: "_runtimeWarning")
public static let runtimeWarning = Self("ComposableArchitecture.runtimeWarning")

Expand Down
2 changes: 1 addition & 1 deletion Sources/ComposableArchitecture/Macros.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public macro Reducer(state: _SynthesizedConformance..., action: _SynthesizedConf
/// the ``Reducer()`` macro.
///
/// See <doc:Reducers#Synthesizing-protocol-conformances-on-State-and-Action> for more information.
@_documentation(visibility:public)
@_documentation(visibility: public)
public struct _SynthesizedConformance {}

extension _SynthesizedConformance {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
let id = self.data.ids[position]
var element = self.data[position]
return self.store.scope(
id: self.store.id(state: \.[id:id]!, action: \.[id:id]),
id: self.store.id(state: \.[id: id]!, action: \.[id: id]),
state: ToState {
element = $0[id: id] ?? element
return element
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,13 @@ import SwiftUI
id: self.store.id(
state:
\.[
id:component.id,fileID:_HashableStaticString(
rawValue: fileID),filePath:_HashableStaticString(
rawValue: filePath),line:line,column:column
id: component.id,
fileID: _HashableStaticString(
rawValue: fileID),
filePath: _HashableStaticString(
rawValue: filePath), line: line, column: column
],
action: \.[id:component.id]
action: \.[id: component.id]
),
state: ToState {
element = $0[id: component.id] ?? element
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public protocol ViewAction<ViewAction> {
#if swift(<5.10)
@MainActor(unsafe)
#else
@preconcurrency @MainActor
@preconcurrency@MainActor
#endif
public protocol ViewActionSending<StoreState, StoreAction> {
associatedtype StoreState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ extension Reducer {
/// - Returns: A reducer that prints debug messages for all received actions.
@inlinable
@warn_unqualified_access
@_documentation(visibility:public)
@_documentation(visibility: public)
public func _printChanges(
_ printer: _ReducerPrinter<State, Action>? = .customDump
) -> _PrintChangesReducer<Self> {
Expand Down
2 changes: 1 addition & 1 deletion Sources/ComposableArchitecture/SharedState/Shared.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public struct Shared<Value> {
else { return nil }
self.init(
reference: base.reference,
keyPath: base.keyPath.appending(path: \Value?.[default:DefaultSubscript(initialValue)])!
keyPath: base.keyPath.appending(path: \Value?.[default: DefaultSubscript(initialValue)])!
)
}

Expand Down
Loading