-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #22 from dirtyhenry/update-tca
Bring TCA tutorial content
- Loading branch information
Showing
9 changed files
with
340 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
.DS_Store | ||
/.build | ||
.build/ | ||
/Packages | ||
/*.xcodeproj | ||
xcuserdata/ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
140 changes: 140 additions & 0 deletions
140
Examples/HoodsApp/HoodsApp/TCATutorial/CounterFeature.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
import ComposableArchitecture | ||
import SwiftUI | ||
|
||
@Reducer | ||
struct CounterFeature { | ||
// An equatable state is required by the TestStore | ||
@ObservableState | ||
struct State: Equatable { | ||
var count = 0 | ||
var fact: String? | ||
var isLoading = false | ||
var isTimerRunning = false | ||
} | ||
|
||
enum Action { | ||
case decrementButtonTapped | ||
case incrementButtonTapped | ||
case factButtonTapped | ||
case factResponse(String) | ||
case timerTick | ||
case toggleTimerButtonTapped | ||
} | ||
|
||
enum CancelID { | ||
case timer | ||
} | ||
|
||
@Dependency(\.continuousClock) var clock | ||
@Dependency(\.numberFact) var numberFact | ||
|
||
var body: some ReducerOf<Self> { | ||
Reduce { state, action in | ||
switch action { | ||
case .decrementButtonTapped: | ||
state.count -= 1 | ||
state.fact = nil | ||
return .none | ||
|
||
case .factButtonTapped: | ||
state.fact = nil | ||
state.isLoading = true | ||
return .run { [count = state.count] send in | ||
try await send(.factResponse(numberFact.fetch(count))) | ||
} | ||
|
||
case let .factResponse(fact): | ||
state.fact = fact | ||
state.isLoading = false | ||
return .none | ||
|
||
case .incrementButtonTapped: | ||
state.count += 1 | ||
state.fact = nil | ||
return .none | ||
|
||
case .timerTick: | ||
state.count += 1 | ||
state.fact = nil | ||
return .none | ||
|
||
case .toggleTimerButtonTapped: | ||
state.isTimerRunning.toggle() | ||
if state.isTimerRunning { | ||
return .run { send in | ||
for await _ in clock.timer(interval: .seconds(1)) { | ||
await send(.timerTick) | ||
} | ||
} | ||
.cancellable(id: CancelID.timer) | ||
} else { | ||
return .cancel(id: CancelID.timer) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
struct CounterView: View { | ||
let store: StoreOf<CounterFeature> | ||
|
||
var body: some View { | ||
VStack { | ||
Text("\(store.count)") | ||
.font(.largeTitle) | ||
.padding() | ||
.background(Color.black.opacity(0.1)) | ||
.cornerRadius(10) | ||
HStack { | ||
Button("-") { | ||
store.send(.decrementButtonTapped) | ||
} | ||
.font(.largeTitle) | ||
.padding() | ||
.background(Color.black.opacity(0.1)) | ||
.cornerRadius(10) | ||
|
||
Button("+") { | ||
store.send(.incrementButtonTapped) | ||
} | ||
.font(.largeTitle) | ||
.padding() | ||
.background(Color.black.opacity(0.1)) | ||
.cornerRadius(10) | ||
} | ||
|
||
Button(store.isTimerRunning ? "Stop timer" : "Start timer") { | ||
store.send(.toggleTimerButtonTapped) | ||
} | ||
.font(.largeTitle) | ||
.padding() | ||
.background(Color.black.opacity(0.1)) | ||
.cornerRadius(10) | ||
|
||
Button("Fact") { | ||
store.send(.factButtonTapped) | ||
} | ||
.font(.largeTitle) | ||
.padding() | ||
.background(Color.black.opacity(0.1)) | ||
.cornerRadius(10) | ||
|
||
if store.isLoading { | ||
ProgressView() | ||
} else if let fact = store.fact { | ||
Text(fact) | ||
.font(.largeTitle) | ||
.multilineTextAlignment(.center) | ||
.padding() | ||
} | ||
} | ||
} | ||
} | ||
|
||
#Preview { | ||
CounterView( | ||
store: Store(initialState: CounterFeature.State()) { | ||
CounterFeature() | ||
} | ||
) | ||
} |
52 changes: 52 additions & 0 deletions
52
Examples/HoodsApp/HoodsApp/TCATutorial/CounterTabFeature.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import ComposableArchitecture | ||
import SwiftUI | ||
|
||
@Reducer | ||
struct CounterTabFeature { | ||
struct State: Equatable { | ||
var tab1 = CounterFeature.State() | ||
var tab2 = CounterFeature.State() | ||
} | ||
|
||
enum Action { | ||
case tab1(CounterFeature.Action) | ||
case tab2(CounterFeature.Action) | ||
} | ||
|
||
var body: some ReducerOf<Self> { | ||
Scope(state: \.tab1, action: \.tab1) { | ||
CounterFeature() | ||
} | ||
Scope(state: \.tab2, action: \.tab2) { | ||
CounterFeature() | ||
} | ||
Reduce { _, _ in | ||
// Core logic of the app feature | ||
.none | ||
} | ||
} | ||
} | ||
|
||
struct CounterTabView: View { | ||
let store: StoreOf<CounterTabFeature> | ||
|
||
var body: some View { | ||
TabView { | ||
Tab("Counter 1", systemImage: "number.square") { | ||
CounterView(store: store.scope(state: \.tab1, action: \.tab1)) | ||
} | ||
|
||
Tab("Counter 2", systemImage: "number.circle.fill") { | ||
CounterView(store: store.scope(state: \.tab2, action: \.tab2)) | ||
} | ||
} | ||
} | ||
} | ||
|
||
#Preview { | ||
CounterTabView( | ||
store: Store(initialState: CounterTabFeature.State()) { | ||
CounterTabFeature() | ||
} | ||
) | ||
} |
23 changes: 23 additions & 0 deletions
23
Examples/HoodsApp/HoodsApp/TCATutorial/NumberFactClient.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import ComposableArchitecture | ||
import Foundation | ||
|
||
struct NumberFactClient { | ||
var fetch: (Int) async throws -> String | ||
} | ||
|
||
extension NumberFactClient: DependencyKey { | ||
static let liveValue = Self( | ||
fetch: { number in | ||
let (data, _) = try await URLSession.shared | ||
.data(from: URL(string: "https://statium-monorepo.vercel.app/newsletter/latest?query=\(number)")!) | ||
return String(decoding: data, as: UTF8.self) | ||
} | ||
) | ||
} | ||
|
||
extension DependencyValues { | ||
var numberFact: NumberFactClient { | ||
get { self[NumberFactClient.self] } | ||
set { self[NumberFactClient.self] = newValue } | ||
} | ||
} |
Oops, something went wrong.