Skip to content

Add swift-clocks to TCA #1547

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

Merged
merged 28 commits into from
Oct 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
push:
branches:
- main
- protocol
- protocol-clocks
pull_request:
branches:
- '*'
Expand Down Expand Up @@ -32,7 +32,7 @@ jobs:
runs-on: macos-12
strategy:
matrix:
xcode: ['13.4.1', '14.0.1']
xcode: ['13.4.1', '14.1']
steps:
- uses: actions/checkout@v3
- name: Select Xcode ${{ matrix.xcode }}
Expand All @@ -44,8 +44,8 @@ jobs:
runs-on: macos-12
steps:
- uses: actions/checkout@v3
- name: Select Xcode ${{ matrix.xcode }}
run: sudo xcode-select -s /Applications/Xcode_14.0.1.app
- name: Select Xcode 14.1
run: sudo xcode-select -s /Applications/Xcode_14.1.app
- name: Run benchmark
run: make benchmark

Expand Down
7 changes: 4 additions & 3 deletions .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ on:
push:
branches:
- main
- protocol-clocks
workflow_dispatch:

concurrency:
Expand All @@ -19,8 +20,8 @@ jobs:
build:
runs-on: macos-12
steps:
- name: Select Xcode 14.0.1
run: sudo xcode-select -s /Applications/Xcode_14.0.1.app
- name: Select Xcode 14.1
run: sudo xcode-select -s /Applications/Xcode_14.1.app

- name: Checkout Package
uses: actions/checkout@v2
Expand All @@ -39,7 +40,7 @@ jobs:
rm -rf docs-out/main;
git tag -l --sort=-v:refname | grep -e "\d\+\.\d\+.0" | tail -n +6 | xargs -I {} rm -rf {};

for tag in $(echo "main"; git tag -l --sort=-v:refname | grep -e "\d\+\.\d\+.0" | head -6);
for tag in $(echo "main"; echo "protocol-clocks"; git tag -l --sort=-v:refname | grep -e "\d\+\.\d\+.0" | head -6);
do
if [ -d "docs-out/$tag/data/documentation/composablearchitecture" ]
then
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Examples/CaseStudies/SwiftUICaseStudies/00-Core.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ struct Root: ReducerProtocol {
case webSocket(WebSocket.Action)
}

@Dependency(\.mainQueue) var mainQueue
@Dependency(\.continuousClock) var clock

var body: some ReducerProtocol<State, Action> {
Reduce { state, action in
Expand All @@ -87,7 +87,7 @@ struct Root: ReducerProtocol {
BindingForm()
}
Scope(state: \.clock, action: /Action.clock) {
Reduce(clockReducer, environment: ClockEnvironment(mainQueue: self.mainQueue))
Reduce(clockReducer, environment: ClockEnvironment(clock: self.clock))
}
Scope(state: \.counter, action: /Action.counter) {
Counter()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ private let readMe = """

To animate changes made to state through a binding, use the `.animation` method on `Binding`.

To animate asynchronous changes made to state via effects, use the `.animation` method provided \
by the CombineSchedulers library to receive asynchronous actions in an animated fashion.
To animate asynchronous changes made to state via effects, use `Effect.run` style of effects \
which allows you to send actions with animations.

Try it out by tapping or dragging anywhere on the screen to move the dot, and by flipping the \
toggle at the bottom of the screen.
Expand All @@ -38,7 +38,7 @@ struct Animations: ReducerProtocol {
case tapped(CGPoint)
}

@Dependency(\.mainQueue) var mainQueue
@Dependency(\.continuousClock) var clock

func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
enum CancelID {}
Expand All @@ -56,7 +56,7 @@ struct Animations: ReducerProtocol {
return .run { send in
for color in [Color.red, .blue, .green, .orange, .pink, .purple, .yellow, .black] {
await send(.setColor(color), animation: .linear)
try await self.mainQueue.sleep(for: 1)
try await self.clock.sleep(for: .seconds(1))
}
}
.cancellable(id: CancelID.self)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ private let readMe = """
uncertainty and complexity.

Many things we do in our applications involve side effects, such as timers, database requests, \
file access, socket connections, and anytime a scheduler is involved (such as debouncing, \
file access, socket connections, and anytime a clock is involved (such as debouncing, \
throttling and delaying), and they are typically difficult to test.

This application has a simple side effect: tapping "Number fact" will trigger an API request to \
Expand All @@ -35,8 +35,8 @@ struct EffectsBasics: ReducerProtocol {
case numberFactResponse(TaskResult<String>)
}

@Dependency(\.continuousClock) var clock
@Dependency(\.factClient) var factClient
@Dependency(\.mainQueue) var mainQueue
private enum DelayID {}

func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
Expand All @@ -48,7 +48,7 @@ struct EffectsBasics: ReducerProtocol {
return state.count >= 0
? .none
: .task {
try await self.mainQueue.sleep(for: 1)
try await self.clock.sleep(for: .seconds(1))
return .decrementDelayResponse
}
.cancellable(id: DelayID.self)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ struct Refreshable: ReducerProtocol {
}

@Dependency(\.factClient) var factClient
@Dependency(\.mainQueue) var mainQueue
private enum FactRequestID {}

func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
Expand Down
12 changes: 6 additions & 6 deletions Examples/CaseStudies/SwiftUICaseStudies/02-Effects-Timers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import ComposableArchitecture
private let readMe = """
This application demonstrates how to work with timers in the Composable Architecture.

It makes use of the `.timer` method on Combine Schedulers, which is a helper provided by the \
Combine Schedulers library included with this library. The helper provides an \
`AsyncSequence`-friendly API for dealing with timers in asynchronous code.
It makes use of the `.timer` method on clocks, which is a helper provided by the Swift Clocks \
library included with this library. The helper provides an `AsyncSequence`-friendly API for \
dealing with times in asynchronous code.
"""

// MARK: - Feature domain
Expand All @@ -23,7 +23,7 @@ struct Timers: ReducerProtocol {
case toggleTimerButtonTapped
}

@Dependency(\.mainQueue) var mainQueue
@Dependency(\.continuousClock) var clock
private enum TimerID {}

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

var body: some View {
WithViewStore(store) { viewStore in
WithViewStore(self.store) { viewStore in
Form {
AboutView(readMe: readMe)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ struct WebSocket: ReducerProtocol {
case webSocket(WebSocketClient.Action)
}

@Dependency(\.mainQueue) var mainQueue
@Dependency(\.continuousClock) var clock
@Dependency(\.webSocket) var webSocket
private enum WebSocketID {}

Expand Down Expand Up @@ -68,7 +68,7 @@ struct WebSocket: ReducerProtocol {
case .didOpen:
group.addTask {
while !Task.isCancelled {
try await self.mainQueue.sleep(for: .seconds(10))
try await self.clock.sleep(for: .seconds(10))
try? await self.webSocket.sendPing(WebSocketID.self)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ struct LoadThenNavigateList: ReducerProtocol {
case setNavigationSelectionDelayCompleted(UUID)
}

@Dependency(\.mainQueue) var mainQueue
@Dependency(\.continuousClock) var clock
private enum CancelID {}

var body: some ReducerProtocol<State, Action> {
Expand All @@ -51,7 +51,7 @@ struct LoadThenNavigateList: ReducerProtocol {
state.rows[id: row.id]?.isActivityIndicatorVisible = row.id == navigatedId
}
return .task {
try await self.mainQueue.sleep(for: 1)
try await self.clock.sleep(for: .seconds(1))
return .setNavigationSelectionDelayCompleted(navigatedId)
}
.cancellable(id: CancelID.self, cancelInFlight: true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ struct NavigateAndLoadList: ReducerProtocol {
case setNavigationSelectionDelayCompleted
}

@Dependency(\.mainQueue) var mainQueue
@Dependency(\.continuousClock) var clock
private enum CancelID {}

var body: some ReducerProtocol<State, Action> {
Expand All @@ -43,7 +43,7 @@ struct NavigateAndLoadList: ReducerProtocol {
case let .setNavigation(selection: .some(id)):
state.selection = Identified(nil, id: id)
return .task {
try await self.mainQueue.sleep(for: 1)
try await self.clock.sleep(for: .seconds(1))
return .setNavigationSelectionDelayCompleted
}
.cancellable(id: CancelID.self, cancelInFlight: true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ struct LoadThenNavigate: ReducerProtocol {
case setNavigationIsActiveDelayCompleted
}

@Dependency(\.mainQueue) var mainQueue
@Dependency(\.continuousClock) var clock
private enum CancelID {}

var body: some ReducerProtocol<State, Action> {
Expand All @@ -38,7 +38,7 @@ struct LoadThenNavigate: ReducerProtocol {
case .setNavigation(isActive: true):
state.isActivityIndicatorVisible = true
return .task {
try await self.mainQueue.sleep(for: 1)
try await self.clock.sleep(for: .seconds(1))
return .setNavigationIsActiveDelayCompleted
}
.cancellable(id: CancelID.self)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ struct NavigateAndLoad: ReducerProtocol {
case setNavigationIsActiveDelayCompleted
}

@Dependency(\.mainQueue) var mainQueue
@Dependency(\.continuousClock) var clock
private enum CancelID {}

var body: some ReducerProtocol<State, Action> {
Expand All @@ -31,7 +31,7 @@ struct NavigateAndLoad: ReducerProtocol {
case .setNavigation(isActive: true):
state.isNavigationActive = true
return .task {
try await self.mainQueue.sleep(for: 1)
try await self.clock.sleep(for: .seconds(1))
return .setNavigationIsActiveDelayCompleted
}
.cancellable(id: CancelID.self)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ struct LoadThenPresent: ReducerProtocol {
case setSheetIsPresentedDelayCompleted
}

@Dependency(\.mainQueue) var mainQueue
@Dependency(\.continuousClock) var clock
private enum CancelID {}

var body: some ReducerProtocol<State, Action> {
Expand All @@ -38,7 +38,7 @@ struct LoadThenPresent: ReducerProtocol {
case .setSheet(isPresented: true):
state.isActivityIndicatorVisible = true
return .task {
try await self.mainQueue.sleep(for: 1)
try await self.clock.sleep(for: .seconds(1))
return .setSheetIsPresentedDelayCompleted
}
.cancellable(id: CancelID.self)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ struct PresentAndLoad: ReducerProtocol {
case setSheetIsPresentedDelayCompleted
}

@Dependency(\.mainQueue) var mainQueue
@Dependency(\.continuousClock) var clock
private enum CancelID {}

var body: some ReducerProtocol<State, Action> {
Expand All @@ -31,7 +31,7 @@ struct PresentAndLoad: ReducerProtocol {
case .setSheet(isPresented: true):
state.isSheetPresented = true
return .task {
try await self.mainQueue.sleep(for: 1)
try await self.clock.sleep(for: .seconds(1))
return .setSheetIsPresentedDelayCompleted
}
.cancellable(id: CancelID.self)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ enum ClockAction: Equatable {
}

struct ClockEnvironment {
var mainQueue: AnySchedulerOf<DispatchQueue>
var clock: any Clock<Duration>
}

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

var body: some View {
WithViewStore(store) { viewStore in
WithViewStore(self.store) { viewStore in
Form {
AboutView(readMe: readMe)

Expand Down Expand Up @@ -147,7 +147,7 @@ struct Subscriptions_Previews: PreviewProvider {
initialState: ClockState(),
reducer: clockReducer,
environment: ClockEnvironment(
mainQueue: .main
clock: ContinuousClock()
)
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ struct LifecycleDemo: ReducerProtocol {
case toggleTimerButtonTapped
}

@Dependency(\.mainQueue) var mainQueue
@Dependency(\.continuousClock) var clock
private enum CancelID {}

var body: some ReducerProtocol<State, Action> {
Expand All @@ -83,7 +83,7 @@ struct LifecycleDemo: ReducerProtocol {
Timer()
.lifecycle(
onAppear: .run { send in
for await _ in self.mainQueue.timer(interval: 1) {
for await _ in self.clock.timer(interval: .seconds(1)) {
await send(.tick)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ struct CityMap: ReducerProtocol {

struct CityMapEnvironment {
var downloadClient: DownloadClient
var mainQueue: AnySchedulerOf<DispatchQueue>
}

var body: some ReducerProtocol<State, Action> {
Expand Down
Loading