Skip to content

Commit 552e757

Browse files
authored
Remove mainActorNow tool where not needed. (#3788)
* Remove mainActorNow tool where not needed. * wip
1 parent 2918fcf commit 552e757

File tree

5 files changed

+85
-64
lines changed

5 files changed

+85
-64
lines changed
Lines changed: 17 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,24 @@
1-
import Dispatch
1+
#if compiler(<6.2)
2+
import Dispatch
23

3-
func mainActorNow<R: Sendable>(execute block: @MainActor @Sendable () -> R) -> R {
4-
if DispatchQueue.getSpecific(key: key) == value {
5-
return MainActor._assumeIsolated {
6-
block()
7-
}
8-
} else {
9-
return DispatchQueue.main.sync {
10-
MainActor._assumeIsolated {
4+
func mainActorNow<R: Sendable>(execute block: @MainActor @Sendable () -> R) -> R {
5+
if DispatchQueue.getSpecific(key: key) == value {
6+
return MainActor._assumeIsolated {
117
block()
128
}
13-
}
14-
}
15-
}
16-
17-
func mainActorASAP(execute block: @escaping @MainActor @Sendable () -> Void) {
18-
if DispatchQueue.getSpecific(key: key) == value {
19-
MainActor._assumeIsolated {
20-
block()
21-
}
22-
} else {
23-
DispatchQueue.main.async {
24-
MainActor._assumeIsolated {
25-
block()
9+
} else {
10+
return DispatchQueue.main.sync {
11+
MainActor._assumeIsolated {
12+
block()
13+
}
2614
}
2715
}
2816
}
29-
}
3017

31-
private let key: DispatchSpecificKey<UInt8> = {
32-
let key = DispatchSpecificKey<UInt8>()
33-
DispatchQueue.main.setSpecific(key: key, value: value)
34-
return key
35-
}()
36-
private let value: UInt8 = 0
18+
private let key: DispatchSpecificKey<UInt8> = {
19+
let key = DispatchSpecificKey<UInt8>()
20+
DispatchQueue.main.setSpecific(key: key, value: value)
21+
return key
22+
}()
23+
private let value: UInt8 = 0
24+
#endif

Sources/ComposableArchitecture/Observation/Binding+Observation.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,25 +100,25 @@ extension BindingAction {
100100
self.isInvalidated = isInvalidated
101101
}
102102
deinit {
103-
let isInvalidated = mainActorNow(execute: isInvalidated)
104-
guard !isInvalidated else { return }
105-
guard wasCalled.value else {
103+
guard !wasCalled.value
104+
else { return }
105+
Task { @MainActor [value, isInvalidated] in
106+
guard !isInvalidated() else { return }
106107
var valueDump: String {
107108
var valueDump = ""
108-
customDump(self.value, to: &valueDump, maxDepth: 0)
109+
customDump(value, to: &valueDump, maxDepth: 0)
109110
return valueDump
110111
}
111112
reportIssue(
112113
"""
113114
A binding action sent from a store was not handled. …
114-
115+
115116
Action:
116117
\(typeName(Action.self)).binding(.set(_, \(valueDump)))
117-
118+
118119
To fix this, invoke "BindingReducer()" from your feature reducer's "body".
119120
"""
120121
)
121-
return
122122
}
123123
}
124124
}

Sources/ComposableArchitecture/SwiftUI/Binding.swift

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -770,22 +770,28 @@ extension WithViewStore where ViewState: Equatable, Content: View {
770770
}
771771

772772
deinit {
773-
let isInvalidated = mainActorNow(execute: isInvalidated)
774-
guard !isInvalidated else { return }
775-
guard self.wasCalled.value else {
773+
guard !self.wasCalled.value
774+
else { return }
775+
776+
Task {
777+
@MainActor [
778+
context, fileID, filePath, line, column, value, bindableActionType, isInvalidated
779+
] in
780+
let tmp = isInvalidated()
781+
guard !tmp else { return }
776782
var valueDump: String {
777783
var valueDump = ""
778-
customDump(self.value, to: &valueDump, maxDepth: 0)
784+
customDump(value, to: &valueDump, maxDepth: 0)
779785
return valueDump
780786
}
781787
reportIssue(
782788
"""
783789
A binding action sent from a store \
784-
\(self.context == .bindingState ? "for binding state defined " : "")at \
785-
"\(self.fileID):\(self.line)" was not handled. …
790+
\(context == .bindingState ? "for binding state defined " : "")at \
791+
"\(fileID):\(line)" was not handled. …
786792
787793
Action:
788-
\(typeName(self.bindableActionType)).binding(.set(_, \(valueDump)))
794+
\(typeName(bindableActionType)).binding(.set(_, \(valueDump)))
789795
790796
To fix this, invoke "BindingReducer()" from your feature reducer's "body".
791797
""",
@@ -794,7 +800,6 @@ extension WithViewStore where ViewState: Equatable, Content: View {
794800
line: line,
795801
column: column
796802
)
797-
return
798803
}
799804
}
800805
}

Sources/ComposableArchitecture/TestStore.swift

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,7 @@ import IssueReporting
429429
#if swift(<5.10)
430430
@MainActor(unsafe)
431431
#else
432-
@preconcurrency@MainActor
432+
@preconcurrency @MainActor
433433
#endif
434434
public final class TestStore<State: Equatable, Action> {
435435
/// The current dependencies of the test store.
@@ -576,7 +576,11 @@ public final class TestStore<State: Equatable, Action> {
576576
column: UInt = #column
577577
) async {
578578
await self.finish(
579-
timeout: duration.nanoseconds, fileID: fileID, file: filePath, line: line, column: column
579+
timeout: duration.nanoseconds,
580+
fileID: fileID,
581+
file: filePath,
582+
line: line,
583+
column: column
580584
)
581585
}
582586

@@ -649,7 +653,10 @@ public final class TestStore<State: Equatable, Action> {
649653

650654
func completed() {
651655
self.assertNoReceivedActions(
652-
fileID: self.fileID, filePath: self.filePath, line: self.line, column: self.column
656+
fileID: self.fileID,
657+
filePath: self.filePath,
658+
line: self.line,
659+
column: self.column
653660
)
654661
Task.cancel(id: OnFirstAppearID())
655662
for effect in self.reducer.inFlightEffects {
@@ -983,7 +990,11 @@ extension TestStore {
983990
let previousStackElementID = self.reducer.dependencies.stackElementID.incrementingCopy()
984991
let task = self.store.send(
985992
.init(
986-
origin: .send(action), fileID: fileID, filePath: filePath, line: line, column: column
993+
origin: .send(action),
994+
fileID: fileID,
995+
filePath: filePath,
996+
line: line,
997+
column: column
987998
)
988999
)
9891000
if uncheckedUseMainSerialExecutor {
@@ -2379,7 +2390,11 @@ extension TestStore {
23792390
await Task.megaYield()
23802391
_ = {
23812392
self._skipReceivedActions(
2382-
strict: strict, fileID: fileID, file: filePath, line: line, column: column
2393+
strict: strict,
2394+
fileID: fileID,
2395+
file: filePath,
2396+
line: line,
2397+
column: column
23832398
)
23842399
}()
23852400
}
@@ -2464,7 +2479,11 @@ extension TestStore {
24642479
await Task.megaYield()
24652480
_ = {
24662481
self._skipInFlightEffects(
2467-
strict: strict, fileID: fileID, filePath: filePath, line: line, column: column
2482+
strict: strict,
2483+
fileID: fileID,
2484+
filePath: filePath,
2485+
line: line,
2486+
column: column
24682487
)
24692488
}()
24702489
}
@@ -2526,7 +2545,7 @@ extension TestStore {
25262545
switch exhaustivity {
25272546
case .on:
25282547
reportIssue(message, fileID: fileID, filePath: filePath, line: line, column: column)
2529-
case let .off(showSkippedAssertions):
2548+
case .off(let showSkippedAssertions):
25302549
if showSkippedAssertions {
25312550
withExpectedIssue {
25322551
reportIssue(
@@ -2833,11 +2852,11 @@ class TestReducer<State: Equatable, Action>: Reducer {
28332852

28342853
let effects: Effect<Action>
28352854
switch action.origin {
2836-
case let .send(action):
2855+
case .send(let action):
28372856
effects = reducer.reduce(into: &state, action: action)
28382857
self.state = state
28392858

2840-
case let .receive(action):
2859+
case .receive(let action):
28412860
effects = reducer.reduce(into: &state, action: action)
28422861
self.receivedActions.append((action, state))
28432862
}
@@ -2908,7 +2927,7 @@ class TestReducer<State: Equatable, Action>: Reducer {
29082927
case send(Action)
29092928
fileprivate var action: Action {
29102929
switch self {
2911-
case let .receive(action), let .send(action):
2930+
case .receive(let action), .send(let action):
29122931
return action
29132932
}
29142933
}

Tests/ComposableArchitectureTests/RuntimeWarningTests.swift

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
final class RuntimeWarningTests: BaseTCATestCase {
77
@MainActor
8-
func testBindingUnhandledAction() {
8+
func testBindingUnhandledAction() async throws {
99
let line = #line + 2
1010
struct State: Equatable {
1111
@BindingState var value = 0
@@ -16,8 +16,6 @@
1616
let store = Store<State, Action>(initialState: State()) {}
1717

1818
XCTExpectFailure {
19-
ViewStore(store, observe: { $0 }).$value.wrappedValue = 42
20-
} issueMatcher: {
2119
$0.compactDescription == """
2220
failed - A binding action sent from a store for binding state defined at \
2321
"\(#fileID):\(line)" was not handled. …
@@ -28,23 +26,26 @@
2826
To fix this, invoke "BindingReducer()" from your feature reducer's "body".
2927
"""
3028
}
29+
30+
let viewStore = ViewStore(store, observe: { $0 })
31+
viewStore.$value.wrappedValue = 42
32+
try await Task.sleep(nanoseconds: 1_000_000)
33+
3134
}
3235

3336
@ObservableState
3437
struct TestObservableBindingUnhandledActionState: Equatable {
3538
var count = 0
3639
}
3740
@MainActor
38-
func testObservableBindingUnhandledAction() {
41+
func testObservableBindingUnhandledAction() async throws {
3942
typealias State = TestObservableBindingUnhandledActionState
4043
enum Action: BindableAction, Equatable {
4144
case binding(BindingAction<State>)
4245
}
4346
let store = Store<State, Action>(initialState: State()) {}
4447

4548
XCTExpectFailure {
46-
store.count = 42
47-
} issueMatcher: {
4849
$0.compactDescription == """
4950
failed - A binding action sent from a store was not handled. …
5051
@@ -54,10 +55,13 @@
5455
To fix this, invoke "BindingReducer()" from your feature reducer's "body".
5556
"""
5657
}
58+
59+
store.count = 42
60+
try await Task.sleep(nanoseconds: 1_000_000)
5761
}
5862

5963
@MainActor
60-
func testBindingUnhandledAction_BindingState() {
64+
func testBindingUnhandledAction_BindingState() async throws {
6165
struct State: Equatable {
6266
@BindingState var value = 0
6367
}
@@ -68,8 +72,6 @@
6872
let store = Store<State, Action>(initialState: State()) {}
6973

7074
XCTExpectFailure {
71-
ViewStore(store, observe: { $0 }).$value.wrappedValue = 42
72-
} issueMatcher: {
7375
$0.compactDescription == """
7476
failed - A binding action sent from a store for binding state defined at \
7577
"\(#fileID):\(line)" was not handled. …
@@ -80,6 +82,10 @@
8082
To fix this, invoke "BindingReducer()" from your feature reducer's "body".
8183
"""
8284
}
85+
86+
let viewStore = ViewStore(store, observe: { $0 })
87+
viewStore.$value.wrappedValue = 42
88+
try await Task.sleep(nanoseconds: 1_000_000)
8389
}
8490

8591
@Reducer
@@ -100,7 +106,10 @@
100106

101107
XCTExpectFailure {
102108
store.scope(state: \.path, action: \.path)[
103-
fileID: "file.swift", filePath: "/file.swift", line: 1, column: 1
109+
fileID: "file.swift",
110+
filePath: "/file.swift",
111+
line: 1,
112+
column: 1
104113
] = .init()
105114
} issueMatcher: {
106115
$0.compactDescription == """

0 commit comments

Comments
 (0)