Skip to content

Commit 4ece153

Browse files
mbrandonwkgrigsby59stephencelis
authored
Add swift-clocks to TCA (#1547)
* Convert schedulers to clocks. * wip * wip * wip * wip * Update 01-GettingStarted-Animations.swift (#1512) Typo * bump * wip * wip * fix * doc fix * wip * update * update * wip * wip * wip * wip * wip * wip * wip * update clocks * wip * clocks * wip * wip Co-authored-by: Ken Grigsby <[email protected]> Co-authored-by: Stephen Celis <[email protected]>
1 parent 95af6c1 commit 4ece153

File tree

58 files changed

+503
-430
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+503
-430
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ on:
44
push:
55
branches:
66
- main
7-
- protocol
7+
- protocol-clocks
88
pull_request:
99
branches:
1010
- '*'
@@ -32,7 +32,7 @@ jobs:
3232
runs-on: macos-12
3333
strategy:
3434
matrix:
35-
xcode: ['13.4.1', '14.0.1']
35+
xcode: ['13.4.1', '14.1']
3636
steps:
3737
- uses: actions/checkout@v3
3838
- name: Select Xcode ${{ matrix.xcode }}
@@ -44,8 +44,8 @@ jobs:
4444
runs-on: macos-12
4545
steps:
4646
- uses: actions/checkout@v3
47-
- name: Select Xcode ${{ matrix.xcode }}
48-
run: sudo xcode-select -s /Applications/Xcode_14.0.1.app
47+
- name: Select Xcode 14.1
48+
run: sudo xcode-select -s /Applications/Xcode_14.1.app
4949
- name: Run benchmark
5050
run: make benchmark
5151

.github/workflows/documentation.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ on:
99
push:
1010
branches:
1111
- main
12+
- protocol-clocks
1213
workflow_dispatch:
1314

1415
concurrency:
@@ -19,8 +20,8 @@ jobs:
1920
build:
2021
runs-on: macos-12
2122
steps:
22-
- name: Select Xcode 14.0.1
23-
run: sudo xcode-select -s /Applications/Xcode_14.0.1.app
23+
- name: Select Xcode 14.1
24+
run: sudo xcode-select -s /Applications/Xcode_14.1.app
2425

2526
- name: Checkout Package
2627
uses: actions/checkout@v2
@@ -39,7 +40,7 @@ jobs:
3940
rm -rf docs-out/main;
4041
git tag -l --sort=-v:refname | grep -e "\d\+\.\d\+.0" | tail -n +6 | xargs -I {} rm -rf {};
4142
42-
for tag in $(echo "main"; git tag -l --sort=-v:refname | grep -e "\d\+\.\d\+.0" | head -6);
43+
for tag in $(echo "main"; echo "protocol-clocks"; git tag -l --sort=-v:refname | grep -e "\d\+\.\d\+.0" | head -6);
4344
do
4445
if [ -d "docs-out/$tag/data/documentation/composablearchitecture" ]
4546
then

ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Examples/CaseStudies/SwiftUICaseStudies/00-Core.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ struct Root: ReducerProtocol {
6060
case webSocket(WebSocket.Action)
6161
}
6262

63-
@Dependency(\.mainQueue) var mainQueue
63+
@Dependency(\.continuousClock) var clock
6464

6565
var body: some ReducerProtocol<State, Action> {
6666
Reduce { state, action in
@@ -87,7 +87,7 @@ struct Root: ReducerProtocol {
8787
BindingForm()
8888
}
8989
Scope(state: \.clock, action: /Action.clock) {
90-
Reduce(clockReducer, environment: ClockEnvironment(mainQueue: self.mainQueue))
90+
Reduce(clockReducer, environment: ClockEnvironment(clock: self.clock))
9191
}
9292
Scope(state: \.counter, action: /Action.counter) {
9393
Counter()

Examples/CaseStudies/SwiftUICaseStudies/01-GettingStarted-Animations.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ private let readMe = """
1111
1212
To animate changes made to state through a binding, use the `.animation` method on `Binding`.
1313
14-
To animate asynchronous changes made to state via effects, use the `.animation` method provided \
15-
by the CombineSchedulers library to receive asynchronous actions in an animated fashion.
14+
To animate asynchronous changes made to state via effects, use `Effect.run` style of effects \
15+
which allows you to send actions with animations.
1616
1717
Try it out by tapping or dragging anywhere on the screen to move the dot, and by flipping the \
1818
toggle at the bottom of the screen.
@@ -38,7 +38,7 @@ struct Animations: ReducerProtocol {
3838
case tapped(CGPoint)
3939
}
4040

41-
@Dependency(\.mainQueue) var mainQueue
41+
@Dependency(\.continuousClock) var clock
4242

4343
func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
4444
enum CancelID {}
@@ -56,7 +56,7 @@ struct Animations: ReducerProtocol {
5656
return .run { send in
5757
for color in [Color.red, .blue, .green, .orange, .pink, .purple, .yellow, .black] {
5858
await send(.setColor(color), animation: .linear)
59-
try await self.mainQueue.sleep(for: 1)
59+
try await self.clock.sleep(for: .seconds(1))
6060
}
6161
}
6262
.cancellable(id: CancelID.self)

Examples/CaseStudies/SwiftUICaseStudies/02-Effects-Basics.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ private let readMe = """
1010
uncertainty and complexity.
1111
1212
Many things we do in our applications involve side effects, such as timers, database requests, \
13-
file access, socket connections, and anytime a scheduler is involved (such as debouncing, \
13+
file access, socket connections, and anytime a clock is involved (such as debouncing, \
1414
throttling and delaying), and they are typically difficult to test.
1515
1616
This application has a simple side effect: tapping "Number fact" will trigger an API request to \
@@ -35,8 +35,8 @@ struct EffectsBasics: ReducerProtocol {
3535
case numberFactResponse(TaskResult<String>)
3636
}
3737

38+
@Dependency(\.continuousClock) var clock
3839
@Dependency(\.factClient) var factClient
39-
@Dependency(\.mainQueue) var mainQueue
4040
private enum DelayID {}
4141

4242
func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
@@ -48,7 +48,7 @@ struct EffectsBasics: ReducerProtocol {
4848
return state.count >= 0
4949
? .none
5050
: .task {
51-
try await self.mainQueue.sleep(for: 1)
51+
try await self.clock.sleep(for: .seconds(1))
5252
return .decrementDelayResponse
5353
}
5454
.cancellable(id: DelayID.self)

Examples/CaseStudies/SwiftUICaseStudies/02-Effects-Refreshable.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ struct Refreshable: ReducerProtocol {
2828
}
2929

3030
@Dependency(\.factClient) var factClient
31-
@Dependency(\.mainQueue) var mainQueue
3231
private enum FactRequestID {}
3332

3433
func reduce(into state: inout State, action: Action) -> EffectTask<Action> {

Examples/CaseStudies/SwiftUICaseStudies/02-Effects-Timers.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import ComposableArchitecture
44
private let readMe = """
55
This application demonstrates how to work with timers in the Composable Architecture.
66
7-
It makes use of the `.timer` method on Combine Schedulers, which is a helper provided by the \
8-
Combine Schedulers library included with this library. The helper provides an \
9-
`AsyncSequence`-friendly API for dealing with timers in asynchronous code.
7+
It makes use of the `.timer` method on clocks, which is a helper provided by the Swift Clocks \
8+
library included with this library. The helper provides an `AsyncSequence`-friendly API for \
9+
dealing with times in asynchronous code.
1010
"""
1111

1212
// MARK: - Feature domain
@@ -23,7 +23,7 @@ struct Timers: ReducerProtocol {
2323
case toggleTimerButtonTapped
2424
}
2525

26-
@Dependency(\.mainQueue) var mainQueue
26+
@Dependency(\.continuousClock) var clock
2727
private enum TimerID {}
2828

2929
func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
@@ -39,7 +39,7 @@ struct Timers: ReducerProtocol {
3939
state.isTimerActive.toggle()
4040
return .run { [isTimerActive = state.isTimerActive] send in
4141
guard isTimerActive else { return }
42-
for await _ in self.mainQueue.timer(interval: 1) {
42+
for await _ in self.clock.timer(interval: .seconds(1)) {
4343
await send(.timerTicked, animation: .interpolatingSpring(stiffness: 3000, damping: 40))
4444
}
4545
}
@@ -54,7 +54,7 @@ struct TimersView: View {
5454
let store: StoreOf<Timers>
5555

5656
var body: some View {
57-
WithViewStore(store) { viewStore in
57+
WithViewStore(self.store) { viewStore in
5858
Form {
5959
AboutView(readMe: readMe)
6060

Examples/CaseStudies/SwiftUICaseStudies/02-Effects-WebSocket.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ struct WebSocket: ReducerProtocol {
3636
case webSocket(WebSocketClient.Action)
3737
}
3838

39-
@Dependency(\.mainQueue) var mainQueue
39+
@Dependency(\.continuousClock) var clock
4040
@Dependency(\.webSocket) var webSocket
4141
private enum WebSocketID {}
4242

@@ -68,7 +68,7 @@ struct WebSocket: ReducerProtocol {
6868
case .didOpen:
6969
group.addTask {
7070
while !Task.isCancelled {
71-
try await self.mainQueue.sleep(for: .seconds(10))
71+
try await self.clock.sleep(for: .seconds(10))
7272
try? await self.webSocket.sendPing(WebSocketID.self)
7373
}
7474
}

Examples/CaseStudies/SwiftUICaseStudies/03-Navigation-Lists-LoadThenNavigate.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ struct LoadThenNavigateList: ReducerProtocol {
3434
case setNavigationSelectionDelayCompleted(UUID)
3535
}
3636

37-
@Dependency(\.mainQueue) var mainQueue
37+
@Dependency(\.continuousClock) var clock
3838
private enum CancelID {}
3939

4040
var body: some ReducerProtocol<State, Action> {
@@ -51,7 +51,7 @@ struct LoadThenNavigateList: ReducerProtocol {
5151
state.rows[id: row.id]?.isActivityIndicatorVisible = row.id == navigatedId
5252
}
5353
return .task {
54-
try await self.mainQueue.sleep(for: 1)
54+
try await self.clock.sleep(for: .seconds(1))
5555
return .setNavigationSelectionDelayCompleted(navigatedId)
5656
}
5757
.cancellable(id: CancelID.self, cancelInFlight: true)

Examples/CaseStudies/SwiftUICaseStudies/03-Navigation-Lists-NavigateAndLoad.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ struct NavigateAndLoadList: ReducerProtocol {
3131
case setNavigationSelectionDelayCompleted
3232
}
3333

34-
@Dependency(\.mainQueue) var mainQueue
34+
@Dependency(\.continuousClock) var clock
3535
private enum CancelID {}
3636

3737
var body: some ReducerProtocol<State, Action> {
@@ -43,7 +43,7 @@ struct NavigateAndLoadList: ReducerProtocol {
4343
case let .setNavigation(selection: .some(id)):
4444
state.selection = Identified(nil, id: id)
4545
return .task {
46-
try await self.mainQueue.sleep(for: 1)
46+
try await self.clock.sleep(for: .seconds(1))
4747
return .setNavigationSelectionDelayCompleted
4848
}
4949
.cancellable(id: CancelID.self, cancelInFlight: true)

Examples/CaseStudies/SwiftUICaseStudies/03-Navigation-LoadThenNavigate.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ struct LoadThenNavigate: ReducerProtocol {
2626
case setNavigationIsActiveDelayCompleted
2727
}
2828

29-
@Dependency(\.mainQueue) var mainQueue
29+
@Dependency(\.continuousClock) var clock
3030
private enum CancelID {}
3131

3232
var body: some ReducerProtocol<State, Action> {
@@ -38,7 +38,7 @@ struct LoadThenNavigate: ReducerProtocol {
3838
case .setNavigation(isActive: true):
3939
state.isActivityIndicatorVisible = true
4040
return .task {
41-
try await self.mainQueue.sleep(for: 1)
41+
try await self.clock.sleep(for: .seconds(1))
4242
return .setNavigationIsActiveDelayCompleted
4343
}
4444
.cancellable(id: CancelID.self)

Examples/CaseStudies/SwiftUICaseStudies/03-Navigation-NavigateAndLoad.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ struct NavigateAndLoad: ReducerProtocol {
2222
case setNavigationIsActiveDelayCompleted
2323
}
2424

25-
@Dependency(\.mainQueue) var mainQueue
25+
@Dependency(\.continuousClock) var clock
2626
private enum CancelID {}
2727

2828
var body: some ReducerProtocol<State, Action> {
@@ -31,7 +31,7 @@ struct NavigateAndLoad: ReducerProtocol {
3131
case .setNavigation(isActive: true):
3232
state.isNavigationActive = true
3333
return .task {
34-
try await self.mainQueue.sleep(for: 1)
34+
try await self.clock.sleep(for: .seconds(1))
3535
return .setNavigationIsActiveDelayCompleted
3636
}
3737
.cancellable(id: CancelID.self)

Examples/CaseStudies/SwiftUICaseStudies/03-Navigation-Sheet-LoadThenPresent.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ struct LoadThenPresent: ReducerProtocol {
2626
case setSheetIsPresentedDelayCompleted
2727
}
2828

29-
@Dependency(\.mainQueue) var mainQueue
29+
@Dependency(\.continuousClock) var clock
3030
private enum CancelID {}
3131

3232
var body: some ReducerProtocol<State, Action> {
@@ -38,7 +38,7 @@ struct LoadThenPresent: ReducerProtocol {
3838
case .setSheet(isPresented: true):
3939
state.isActivityIndicatorVisible = true
4040
return .task {
41-
try await self.mainQueue.sleep(for: 1)
41+
try await self.clock.sleep(for: .seconds(1))
4242
return .setSheetIsPresentedDelayCompleted
4343
}
4444
.cancellable(id: CancelID.self)

Examples/CaseStudies/SwiftUICaseStudies/03-Navigation-Sheet-PresentAndLoad.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ struct PresentAndLoad: ReducerProtocol {
2222
case setSheetIsPresentedDelayCompleted
2323
}
2424

25-
@Dependency(\.mainQueue) var mainQueue
25+
@Dependency(\.continuousClock) var clock
2626
private enum CancelID {}
2727

2828
var body: some ReducerProtocol<State, Action> {
@@ -31,7 +31,7 @@ struct PresentAndLoad: ReducerProtocol {
3131
case .setSheet(isPresented: true):
3232
state.isSheetPresented = true
3333
return .task {
34-
try await self.mainQueue.sleep(for: 1)
34+
try await self.clock.sleep(for: .seconds(1))
3535
return .setSheetIsPresentedDelayCompleted
3636
}
3737
.cancellable(id: CancelID.self)

Examples/CaseStudies/SwiftUICaseStudies/04-HigherOrderReducers-ElmLikeSubscriptions.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ enum ClockAction: Equatable {
4747
}
4848

4949
struct ClockEnvironment {
50-
var mainQueue: AnySchedulerOf<DispatchQueue>
50+
var clock: any Clock<Duration>
5151
}
5252

5353
let clockReducer = AnyReducer<ClockState, ClockAction, ClockEnvironment>.combine(
@@ -66,7 +66,7 @@ let clockReducer = AnyReducer<ClockState, ClockAction, ClockEnvironment>.combine
6666
struct TimerID: Hashable {}
6767
return [
6868
TimerID(): .run { send in
69-
for await _ in environment.mainQueue.timer(interval: 1) {
69+
for await _ in environment.clock.timer(interval: .seconds(1)) {
7070
await send(.timerTicked, animation: .interpolatingSpring(stiffness: 3000, damping: 40))
7171
}
7272
}
@@ -80,7 +80,7 @@ struct ClockView: View {
8080
let store: Store<ClockState, ClockAction>
8181

8282
var body: some View {
83-
WithViewStore(store) { viewStore in
83+
WithViewStore(self.store) { viewStore in
8484
Form {
8585
AboutView(readMe: readMe)
8686

@@ -147,7 +147,7 @@ struct Subscriptions_Previews: PreviewProvider {
147147
initialState: ClockState(),
148148
reducer: clockReducer,
149149
environment: ClockEnvironment(
150-
mainQueue: .main
150+
clock: ContinuousClock()
151151
)
152152
)
153153
)

Examples/CaseStudies/SwiftUICaseStudies/04-HigherOrderReducers-Lifecycle.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ struct LifecycleDemo: ReducerProtocol {
6464
case toggleTimerButtonTapped
6565
}
6666

67-
@Dependency(\.mainQueue) var mainQueue
67+
@Dependency(\.continuousClock) var clock
6868
private enum CancelID {}
6969

7070
var body: some ReducerProtocol<State, Action> {
@@ -83,7 +83,7 @@ struct LifecycleDemo: ReducerProtocol {
8383
Timer()
8484
.lifecycle(
8585
onAppear: .run { send in
86-
for await _ in self.mainQueue.timer(interval: 1) {
86+
for await _ in self.clock.timer(interval: .seconds(1)) {
8787
await send(.tick)
8888
}
8989
}

Examples/CaseStudies/SwiftUICaseStudies/04-HigherOrderReducers-ResuableOfflineDownloads/ReusableComponents-Download.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ struct CityMap: ReducerProtocol {
5151

5252
struct CityMapEnvironment {
5353
var downloadClient: DownloadClient
54-
var mainQueue: AnySchedulerOf<DispatchQueue>
5554
}
5655

5756
var body: some ReducerProtocol<State, Action> {

0 commit comments

Comments
 (0)