New to TCA, loving it so far! I'm making a toy app that applies TCA. It's just two features: DiceRoller
and Fibonaccizer
. DiceRoller
is just a button that rolls a 6-sided die and displays the result. Fibonaccizer
is just a button that, when clicked, increments n
and displays the n
th Fibonacci number.
Both features work well in isolation (aside from integer overflow when I get to the 92nd Fibonacci number but that isn't the point of my exercise). I want to create a new feature that composes both DiceRoller
and Fibonaccizer
. When I roll the die, I'd like the roll result to be used as n
in Fibonaccizer
.
DiceRoller
:
import Foundation
import SwiftUI
import ComposableArchitecture
@Reducer
struct DiceRollerFeature {
struct State: Equatable {
var diceRollResult: Int?
var diceRollResultString: String {
if let diceRollResult {
return "\(diceRollResult)"
} else {
return "Nothing yet"
}
}
}
enum Action {
case rollButtonTapped
}
@Dependency(\.rng) var rng
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .rollButtonTapped:
state.diceRollResult = self.rng.roll(1, 6)
return .none
}
}
}
}
struct DiceRoller: View {
let store: StoreOf<DiceRollerFeature>
var body: some View {
WithViewStore(store, observe: { $0 }) { viewStore in
VStack {
Text("Dice Roller")
.font(.title)
Text("Result: \(viewStore.diceRollResultString)")
Button("Roll d6") {
viewStore.send(.rollButtonTapped)
}
}
}
}
}
#Preview {
DiceRoller(
store: Store(initialState: DiceRollerFeature.State()) {
DiceRollerFeature()
}
)
}
Fibonaccizer
import Foundation
import SwiftUI
import ComposableArchitecture
@Reducer
struct FibonaccizerFeature {
struct State: Equatable {
var n: Int = 1
var nthFibonacciNumber: Int = 1
}
enum Action {
case fibonaccizeButtonTapped
case receiveNToFibonaccize(Int)
}
@Dependency(\.fib) var fib
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .fibonaccizeButtonTapped:
state.n += 1
state.nthFibonacciNumber = self.fib.getNthFibonacciNumber(state.n)
return .none
case .receiveNToFibonaccize(let n):
state.n = n
state.nthFibonacciNumber = self.fib.getNthFibonacciNumber(state.n)
return .none
}
}
}
}
struct Fibonaccizer: View {
let store: StoreOf<FibonaccizerFeature>
var formatter: NumberFormatter {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .ordinal
return numberFormatter
}
var body: some View {
WithViewStore(store, observe: { $0 }) { viewStore in
VStack {
Text("Fibonaccizer")
.font(.title)
Text("The \(formatter.string(for: viewStore.n)!) Fibonacci number is \(viewStore.nthFibonacciNumber)")
Button("Fibonaccize!") {
viewStore.send(.fibonaccizeButtonTapped)
}
}
}
}
}
#Preview {
Fibonaccizer(
store: Store(
initialState: FibonaccizerFeature.State()) {
FibonaccizerFeature()
}
)
}
And my incomplete attempt at the parent feature/view FibDice
that tries to compose together DiceRoller
and Fibonaccizer
:
import Foundation
import SwiftUI
import ComposableArchitecture
@Reducer
struct FibDiceFeature {
struct State: Equatable {
var diceRollerState = DiceRollerFeature.State()
var fibonaccizerState = FibonaccizerFeature.State()
}
enum Action {
case diceRollerAction(DiceRollerFeature.Action)
case fibonaccizerAction(FibonaccizerFeature.Action)
}
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .diceRollerAction(.rollButtonTapped):
return .none
case .fibonaccizerAction(_):
return .none
}
}
}
}
struct FibDice: View {
let store: StoreOf<FibDiceFeature>
var body: some View {
WithViewStore(store, observe: { $0 }) { viewStore in
DiceRoller(store: viewStore.)
Fibonaccizer(store: store)
}
}
}
#Preview {
FibDice()
}
My questions:
- Am I on the right track by using a parent feature that composes together
DiceRoller
andFibonaccizer
? - What state and actions should I define for the parent feature
FibDice
? - Should the
DiceRoller
orFibonaccizer
know anything about the parent feature? My gut says no since that would make it hard to develop either child feature in isolation.
Thanks!