Last active
March 30, 2024 02:30
-
-
Save lukeredpath/2bf827a04a7d25b8482ff475c24ea5ec to your computer and use it in GitHub Desktop.
Revisions
-
lukeredpath revised this gist
Mar 30, 2024 . 1 changed file with 12 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -10,13 +10,13 @@ import SwiftUI // MARK: - Models struct User: Equatable, Hashable, Sendable, Codable { let id: UUID let name: String } extension User { static var placeholder = User(id: .init(0), name: "") static let joe = User(id: .init(1), name: "Joe") static let bob = User(id: .init(2), name: "Bob") } @@ -27,6 +27,8 @@ extension User { struct AppFeature: Reducer { @ObservableState struct State: Equatable { @Shared(.user) var user var session = Session.State.loggedOut(.init()) } @@ -44,6 +46,9 @@ struct AppFeature: Reducer { state.session = .loggedIn(.init(user: user)) return .none case .session(.loggedIn(.logoutButtonTapped)): // If we want to reset our shared user state as soon as a // user logs out, we can do that here. // state.user = .placeholder state.session = .loggedOut(.init()) return .none case .session: @@ -102,7 +107,11 @@ struct LoggedIn: Reducer { @Reducer struct LoggedOut: Reducer { @ObservableState struct State: Equatable { @Shared(.user) var user } enum Action { case loginButtonTapped(user: User) -
lukeredpath renamed this gist
Mar 30, 2024 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
lukeredpath created this gist
Mar 30, 2024 .There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,220 @@ // // SharedStateDemoApp.swift // SharedStateDemo // // Created by Luke Redpath on 29/03/2024. // import ComposableArchitecture import SwiftUI // MARK: - Models struct User: Equatable, Hashable, Sendable { let id: UUID let name: String } extension User { static let placeholder = User(id: .init(0), name: "") static let joe = User(id: .init(1), name: "Joe") static let bob = User(id: .init(2), name: "Bob") } // MARK: - Reducers @Reducer struct AppFeature: Reducer { @ObservableState struct State: Equatable { var session = Session.State.loggedOut(.init()) } enum Action { case session(Session.Action) } var body: some ReducerOf<Self> { Scope(state: \.session, action: \.session) { Session.body } Reduce<State, Action> { state, action in switch action { case .session(.loggedOut(.loginButtonTapped(let user))): state.session = .loggedIn(.init(user: user)) return .none case .session(.loggedIn(.logoutButtonTapped)): state.session = .loggedOut(.init()) return .none case .session: return .none } } } @Reducer(state: .equatable) enum Session { case loggedIn(LoggedIn) case loggedOut(LoggedOut) } } @Reducer struct LoggedIn: Reducer { @ObservableState struct State: Equatable { @Shared(.user) var user: User @Presents var someLoggedInFeature: SomeLoggedInFeature.State? init(user: User) { // We want to update the shared user every time a new // user logs in to the app. self.user = user } } enum Action { case openSomeFeatureButtonTapped case logoutButtonTapped case someLoggedInFeature(PresentationAction<SomeLoggedInFeature.Action>) } var body: some ReducerOf<Self> { Reduce<State, Action> { state, action in switch action { case .openSomeFeatureButtonTapped: state.someLoggedInFeature = .init(user: state.$user) return .none case .logoutButtonTapped: return .none case .someLoggedInFeature: return .none } } .ifLet(\.$someLoggedInFeature, action: \.someLoggedInFeature) { SomeLoggedInFeature() } } } @Reducer struct LoggedOut: Reducer { struct State: Equatable {} enum Action { case loginButtonTapped(user: User) } } @Reducer struct SomeLoggedInFeature: Reducer { @ObservableState struct State: Equatable { @Shared(.user) var user: User } } // MARK: - Persistence extension PersistenceKey where Self == DefaultProvidingKey<InMemoryKey<User>> { static var user: Self { inMemory("user", defaultValue: .placeholder) } } // MARK: - App and Views @main struct SharedStateDemoApp: App { @Bindable var store = StoreOf<AppFeature>(initialState: AppFeature.State()) { AppFeature()._printChanges() } var body: some Scene { WindowGroup { switch store.scope(state: \.session, action: \.session).case { case let .loggedIn(store): LoggedInView(store: store) case let .loggedOut(store): LoggedOutView(store: store) } } } } struct LoggedInView: View { @Bindable var store: StoreOf<LoggedIn> var body: some View { NavigationStack { List { Section { Text("Logged in as \(store.user.name)") } Section { Button("Open some feature") { store.send(.openSomeFeatureButtonTapped) } } Section { Button("Log Out") { store.send(.logoutButtonTapped) } .foregroundStyle(.red) } } .navigationTitle("Logged In") .navigationBarTitleDisplayMode(.inline) .sheet(item: $store.scope(state: \.someLoggedInFeature, action: \.someLoggedInFeature)) { SomeFeatureView(store: $0) } } } } struct LoggedOutView: View { let store: StoreOf<LoggedOut> var body: some View { NavigationStack { List { Section { Button("Login as Joe") { store.send(.loginButtonTapped(user: .joe)) } Button("Login as Bob") { store.send(.loginButtonTapped(user: .bob)) } } } .navigationTitle("Logged Out") .navigationBarTitleDisplayMode(.inline) } } } struct SomeFeatureView: View { let store: StoreOf<SomeLoggedInFeature> @Environment(\.dismiss) private var dismiss var body: some View { NavigationStack { Text("Hello \(store.user.name)") .navigationTitle("Some Feature") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .primaryAction) { Button("Done") { dismiss() } } } } } }