Created
November 29, 2023 02:08
-
-
Save ocapmycap/b96f04343dc98effae98d15a4e7376ca to your computer and use it in GitHub Desktop.
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
// | |
// Card.swift | |
// ObservableArchitecture | |
// | |
// Created by Brian Lane on 11/28/23. | |
// | |
import SwiftUI | |
import ComposableArchitecture | |
@Reducer | |
public struct CardList: Reducer { | |
// MARK: - Dependencies | |
// MARK: - State | |
@ObservableState | |
public struct State: Equatable { | |
var cards: IdentifiedArrayOf<Card.State> | |
public init(cards: IdentifiedArrayOf<Card.State> = []) { | |
self.cards = cards | |
} | |
} | |
// MARK: - Action | |
public enum Action: Equatable { | |
case addCard | |
case cards(IdentifiedActionOf<Card>) | |
} | |
// MARK: - Reducer | |
public var body: some ReducerOf<Self> { | |
Reduce { state, action in | |
switch action { | |
case .addCard: | |
state.cards.append(Card.State.randomCard()) | |
return .none | |
case .cards(_): | |
return .none | |
} | |
} | |
}} | |
public struct CardListView: View { | |
let store: StoreOf<CardList> | |
public init(store: StoreOf<CardList>) { | |
self.store = store | |
} | |
public var body: some View { | |
WithPerceptionTracking { | |
VStack { | |
Button { | |
store.send(.addCard) | |
} label: { | |
Text("Add card") | |
} | |
List { | |
ForEach(store.scope(state: \.cards, action: \.cards)) { store in | |
CardView(store: store) | |
} | |
} | |
} | |
} | |
} | |
} | |
struct CardListView_Previews: PreviewProvider { | |
static var previews: some View { | |
CardListView( | |
store: Store( | |
initialState: CardList.State(cards: .mock), | |
reducer: { CardList() } | |
) | |
) | |
} | |
} | |
@Reducer | |
public struct Card: Reducer { | |
// MARK: - Dependencies | |
// MARK: - State | |
@ObservableState | |
public enum State: Equatable, Identifiable { | |
case cardA(CardA.State) | |
case cardB(CardB.State) | |
public var id: UUID { | |
switch self { | |
case .cardA(let state): | |
return state.id | |
case .cardB(let state): | |
return state.id | |
} | |
} | |
public init() { | |
self = .cardA(CardA.State()) | |
} | |
public static func randomCard() -> Card.State { | |
return Bool.random() ? .cardA(CardA.State()) : .cardB(CardB.State()) | |
} | |
} | |
// MARK: - Action | |
public enum Action: Equatable { | |
case cardA(CardA.Action) | |
case cardB(CardB.Action) | |
} | |
// MARK: - Reducer | |
public var body: some ReducerOf<Self> { | |
Reduce { state, action in | |
switch action { | |
case .cardA: | |
return .none | |
case .cardB: | |
return .none | |
} | |
} | |
} | |
} | |
public struct CardView: View { | |
let store: StoreOf<Card> | |
public init(store: StoreOf<Card>) { | |
self.store = store | |
} | |
public var body: some View { | |
WithPerceptionTracking { | |
switch store.state { | |
case .cardA(let state): | |
CardAView(store: Store(initialState: state, reducer: { CardA()})) | |
case .cardB(let state): | |
CardBView(store: Store(initialState: state, reducer: { CardB()})) | |
} | |
} | |
} | |
} | |
@Reducer | |
public struct CardA: Reducer { | |
// MARK: - Dependencies | |
// MARK: - State | |
@ObservableState | |
public struct State: Equatable, Identifiable { | |
public let id = UUID() | |
var title: String = "" | |
} | |
// MARK: - Action | |
public enum Action: Equatable { | |
case titleChanged(String) | |
} | |
// MARK: - Reducer | |
public var body: some ReducerOf<Self> { | |
Reduce { state, action in | |
switch action { | |
case .titleChanged(let newValue): | |
state.title = newValue | |
return .none | |
} | |
} | |
} | |
} | |
public struct CardAView: View { | |
@State var store: StoreOf<CardA> | |
public init(store: StoreOf<CardA>) { | |
self.store = store | |
} | |
public var body: some View { | |
TextField("Title", text: $store.title.sending(\.titleChanged)) // <--- could not get simple $store.title to work | |
} | |
} | |
struct CardAView_Previews: PreviewProvider { | |
static var previews: some View { | |
CardAView(store: Store(initialState: CardA.State(), reducer: { CardA() })) | |
} | |
} | |
@Reducer | |
public struct CardB: Reducer { | |
// MARK: - Dependencies | |
// MARK: - State | |
@ObservableState | |
public struct State: Equatable, Identifiable { | |
public let id = UUID() | |
var icon: Icon = .lady | |
public enum Icon { | |
case man | |
case lady | |
public var image: Image { | |
switch self { | |
case .man: | |
return Image(systemName: "figure.strengthtraining.functional") | |
case .lady: | |
return Image(systemName: "ladybug") | |
} | |
} | |
mutating public func toggle() { | |
self = self == .lady ? .man : .lady | |
} | |
} | |
} | |
// MARK: - Action | |
public enum Action: Equatable { | |
case toggle | |
} | |
// MARK: - Reducer | |
public var body: some ReducerOf<Self> { | |
Reduce { state, action in | |
switch action { | |
case .toggle: | |
state.icon.toggle() | |
return .none | |
} | |
} | |
} | |
} | |
public struct CardBView: View { | |
let store: StoreOf<CardB> | |
public init(store: StoreOf<CardB>) { | |
self.store = store | |
} | |
public var body: some View { | |
HStack { | |
store.icon.image | |
Button { | |
store.send(.toggle) | |
} label: { | |
Text("Toggle") | |
} | |
} | |
} | |
} | |
struct CardBView_Previews: PreviewProvider { | |
static var previews: some View { | |
CardBView(store: Store(initialState: CardB.State(), reducer: { CardB() }) | |
) | |
} | |
} | |
extension IdentifiedArray where ID == Card.State.ID, Element == Card.State { | |
static let mock: Self = [ | |
Card.State(), | |
Card.State(), | |
Card.State() | |
] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment