Skip to content

Commit 970fbc0

Browse files
committed
Remove mainActorNow tool where not needed.
1 parent b21a87a commit 970fbc0

File tree

5 files changed

+96
-68
lines changed

5 files changed

+96
-68
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: 40 additions & 14 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

@@ -642,14 +646,24 @@ public final class TestStore<State: Equatable, Action> {
642646
self.assertNoSharedChanges(fileID: fileID, filePath: filePath, line: line, column: column)
643647
}
644648

645-
deinit {
646-
uncheckedUseMainSerialExecutor = self.originalUseMainSerialExecutor
647-
mainActorNow { self.completed() }
648-
}
649+
#if compiler(>=6.2)
650+
isolated deinit {
651+
uncheckedUseMainSerialExecutor = originalUseMainSerialExecutor
652+
completed()
653+
}
654+
#else
655+
deinit {
656+
uncheckedUseMainSerialExecutor = self.originalUseMainSerialExecutor
657+
mainActorNow { self.completed() }
658+
}
659+
#endif
649660

650661
func completed() {
651662
self.assertNoReceivedActions(
652-
fileID: self.fileID, filePath: self.filePath, line: self.line, column: self.column
663+
fileID: self.fileID,
664+
filePath: self.filePath,
665+
line: self.line,
666+
column: self.column
653667
)
654668
Task.cancel(id: OnFirstAppearID())
655669
for effect in self.reducer.inFlightEffects {
@@ -983,7 +997,11 @@ extension TestStore {
983997
let previousStackElementID = self.reducer.dependencies.stackElementID.incrementingCopy()
984998
let task = self.store.send(
985999
.init(
986-
origin: .send(action), fileID: fileID, filePath: filePath, line: line, column: column
1000+
origin: .send(action),
1001+
fileID: fileID,
1002+
filePath: filePath,
1003+
line: line,
1004+
column: column
9871005
)
9881006
)
9891007
if uncheckedUseMainSerialExecutor {
@@ -2379,7 +2397,11 @@ extension TestStore {
23792397
await Task.megaYield()
23802398
_ = {
23812399
self._skipReceivedActions(
2382-
strict: strict, fileID: fileID, file: filePath, line: line, column: column
2400+
strict: strict,
2401+
fileID: fileID,
2402+
file: filePath,
2403+
line: line,
2404+
column: column
23832405
)
23842406
}()
23852407
}
@@ -2464,7 +2486,11 @@ extension TestStore {
24642486
await Task.megaYield()
24652487
_ = {
24662488
self._skipInFlightEffects(
2467-
strict: strict, fileID: fileID, filePath: filePath, line: line, column: column
2489+
strict: strict,
2490+
fileID: fileID,
2491+
filePath: filePath,
2492+
line: line,
2493+
column: column
24682494
)
24692495
}()
24702496
}
@@ -2526,7 +2552,7 @@ extension TestStore {
25262552
switch exhaustivity {
25272553
case .on:
25282554
reportIssue(message, fileID: fileID, filePath: filePath, line: line, column: column)
2529-
case let .off(showSkippedAssertions):
2555+
case .off(let showSkippedAssertions):
25302556
if showSkippedAssertions {
25312557
withExpectedIssue {
25322558
reportIssue(
@@ -2833,11 +2859,11 @@ class TestReducer<State: Equatable, Action>: Reducer {
28332859

28342860
let effects: Effect<Action>
28352861
switch action.origin {
2836-
case let .send(action):
2862+
case .send(let action):
28372863
effects = reducer.reduce(into: &state, action: action)
28382864
self.state = state
28392865

2840-
case let .receive(action):
2866+
case .receive(let action):
28412867
effects = reducer.reduce(into: &state, action: action)
28422868
self.receivedActions.append((action, state))
28432869
}
@@ -2908,7 +2934,7 @@ class TestReducer<State: Equatable, Action>: Reducer {
29082934
case send(Action)
29092935
fileprivate var action: Action {
29102936
switch self {
2911-
case let .receive(action), let .send(action):
2937+
case .receive(let action), .send(let action):
29122938
return action
29132939
}
29142940
}

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)