Skip to content
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

Clean up examples #2754

Merged
merged 6 commits into from
Feb 1, 2024
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
134 changes: 106 additions & 28 deletions Examples/CaseStudies/SwiftUICaseStudies/00-RootView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,54 +9,92 @@ struct RootView: View {
Form {
Section {
NavigationLink("Basics") {
CounterDemoView()
Demo(store: Store(initialState: Counter.State()) { Counter() }) { store in
CounterDemoView(store: store)
}
}
NavigationLink("Combining reducers") {
TwoCountersView()
Demo(store: Store(initialState: TwoCounters.State()) { TwoCounters() }) { store in
TwoCountersView(store: store)
}
}
NavigationLink("Bindings") {
BindingBasicsView()
Demo(store: Store(initialState: BindingBasics.State()) { BindingBasics() }) { store in
BindingBasicsView(store: store)
}
}
NavigationLink("Form bindings") {
BindingFormView()
Demo(store: Store(initialState: BindingForm.State()) { BindingForm() }) { store in
BindingFormView(store: store)
}
}
NavigationLink("Optional state") {
OptionalBasicsView()
Demo(store: Store(initialState: OptionalBasics.State()) { OptionalBasics() }) { store in
OptionalBasicsView(store: store)
}
}
NavigationLink("Shared state") {
SharedStateView()
Demo(store: Store(initialState: SharedState.State()) { SharedState() }) { store in
SharedStateView(store: store)
}
}
NavigationLink("Alerts and Confirmation Dialogs") {
AlertAndConfirmationDialogView()
Demo(
store: Store(initialState: AlertAndConfirmationDialog.State()) {
AlertAndConfirmationDialog()
}
) { store in
AlertAndConfirmationDialogView(store: store)
}
}
NavigationLink("Focus State") {
FocusDemoView()
Demo(store: Store(initialState: FocusDemo.State()) { FocusDemo() }) { store in
FocusDemoView(store: store)
}
}
NavigationLink("Animations") {
AnimationsView()
Demo(store: Store(initialState: Animations.State()) { Animations() }) { store in
AnimationsView(store: store)
}
}
} header: {
Text("Getting started")
}

Section {
NavigationLink("Basics") {
EffectsBasicsView()
Demo(store: Store(initialState: EffectsBasics.State()) { EffectsBasics() }) { store in
EffectsBasicsView(store: store)
}
}
NavigationLink("Cancellation") {
EffectsCancellationView()
Demo(
store: Store(initialState: EffectsCancellation.State()) { EffectsCancellation() }
) { store in
EffectsCancellationView(store: store)
}
}
NavigationLink("Long-living effects") {
LongLivingEffectsView()
Demo(
store: Store(initialState: LongLivingEffects.State()) { LongLivingEffects() }
) { store in
LongLivingEffectsView(store: store)
}
}
NavigationLink("Refreshable") {
RefreshableView()
Demo(store: Store(initialState: Refreshable.State()) { Refreshable() }) { store in
RefreshableView(store: store)
}
}
NavigationLink("Timers") {
TimersView()
Demo(store: Store(initialState: Timers.State()) { Timers() }) { store in
TimersView(store: store)
}
}
NavigationLink("Web socket") {
WebSocketView()
Demo(store: Store(initialState: WebSocket.State()) { WebSocket() }) { store in
WebSocketView(store: store)
}
}
} header: {
Text("Effects")
Expand All @@ -69,51 +107,91 @@ struct RootView: View {
.buttonStyle(.plain)

NavigationLink("Navigate and load data") {
NavigateAndLoadView()
Demo(
store: Store(initialState: NavigateAndLoad.State()) { NavigateAndLoad() }
) { store in
NavigateAndLoadView(store: store)
}
}

NavigationLink("Lists: Navigate and load data") {
NavigateAndLoadListView()
Demo(
store: Store(initialState: NavigateAndLoadList.State()) { NavigateAndLoadList() }
) { store in
NavigateAndLoadListView(store: store)
}
}
NavigationLink("Sheets: Present and load data") {
PresentAndLoadView()
Demo(store: Store(initialState: PresentAndLoad.State()) { PresentAndLoad() }) { store in
PresentAndLoadView(store: store)
}
}
NavigationLink("Sheets: Load data then present") {
LoadThenPresentView()
Demo(
store: Store(initialState: LoadThenPresent.State()) { LoadThenPresent() }
) { store in
LoadThenPresentView(store: store)
}
}
NavigationLink("Multiple destinations") {
MultipleDestinationsView()
Demo(
store: Store(initialState: MultipleDestinations.State()) { MultipleDestinations() }
) { store in
MultipleDestinationsView(store: store)
}
}
} header: {
Text("Navigation")
}

Section {
NavigationLink("Reusable favoriting component") {
EpisodesView()
Demo(store: Store(initialState: Episodes.State()) { Episodes() }) { store in
EpisodesView(store: store)
}
}
NavigationLink("Reusable offline download component") {
CitiesView()
Demo(store: Store(initialState: MapApp.State()) { MapApp() }) { store in
CitiesView(store: store)
}
}
NavigationLink("Recursive state and actions") {
NestedView()
Demo(store: Store(initialState: Nested.State()) { Nested() }) { store in
NestedView(store: store)
}
}
} header: {
Text("Higher-order reducers")
}
}
.navigationTitle("Case Studies")
.sheet(isPresented: self.$isNavigationStackCaseStudyPresented) {
NavigationDemoView()
Demo(store: Store(initialState: NavigationDemo.State()) { NavigationDemo() }) { store in
NavigationDemoView(store: store)
}
}
}
}
}

// MARK: - SwiftUI previews
/// This wrapper provides an "entry" point into an individual demo that can own a store.
struct Demo<State, Action, Content: View>: View {
@SwiftUI.State var store: Store<State, Action>
let content: (Store<State, Action>) -> Content

init(
store: Store<State, Action>,
@ViewBuilder content: @escaping (Store<State, Action>) -> Content
) {
self.store = store
self.content = content
}

struct RootView_Previews: PreviewProvider {
static var previews: some View {
RootView()
var body: some View {
self.content(self.store)
}
}

#Preview {
RootView()
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,17 @@ private let readMe = """
This demonstrates how to best handle alerts and confirmation dialogs in the Composable \
Architecture.

Because the library demands that all data flow through the application in a single direction, we \
cannot leverage SwiftUI's two-way bindings because they can make changes to state without going \
through a reducer. This means we can't directly use the standard API to display alerts and sheets.

However, the library comes with two types, `AlertState` and `ConfirmationDialogState`, which can \
be constructed from reducers and control whether or not an alert or confirmation dialog is \
displayed. Further, it automatically handles sending actions when you tap their buttons, which \
allows you to properly handle their functionality in the reducer rather than in two-way bindings \
and action closures.

The benefit of doing this is that you can get full test coverage on how a user interacts with \
alerts and dialogs in your application
The library comes with two types, `AlertState` and `ConfirmationDialogState`, which are data \
descriptions of the state and actions of an alert or dialog. These types can be constructed in \
reducers to control whether or not an alert or confirmation dialog is displayed, and \
corresponding view modifiers, `alert(_:)` and `confirmationDialog(_:)`, can be handed bindings \
to a store focused on an alert or dialog domain so that the alert or dialog can be displayed in \
the view.

The benefit of using these types is that you can get full test coverage on how a user interacts \
with alerts and dialogs in your application
"""

// MARK: - Feature domain

@Reducer
struct AlertAndConfirmationDialog {
@ObservableState
Expand Down Expand Up @@ -104,12 +99,8 @@ struct AlertAndConfirmationDialog {
}
}

// MARK: - Feature view

struct AlertAndConfirmationDialogView: View {
@Bindable var store = Store(initialState: AlertAndConfirmationDialog.State()) {
AlertAndConfirmationDialog()
}
@Bindable var store: StoreOf<AlertAndConfirmationDialog>

var body: some View {
Form {
Expand All @@ -127,16 +118,12 @@ struct AlertAndConfirmationDialogView: View {
}
}

// MARK: - SwiftUI previews

struct AlertAndConfirmationDialog_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
AlertAndConfirmationDialogView(
store: Store(initialState: AlertAndConfirmationDialog.State()) {
AlertAndConfirmationDialog()
}
)
}
#Preview {
NavigationStack {
AlertAndConfirmationDialogView(
store: Store(initialState: AlertAndConfirmationDialog.State()) {
AlertAndConfirmationDialog()
}
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,22 @@ import SwiftUI

private let readMe = """
This screen demonstrates how changes to application state can drive animations. Because the \
`Store` processes actions sent to it synchronously you can typically perform animations \
in the Composable Architecture just as you would in regular SwiftUI.
`Store` processes actions sent to it synchronously you can typically perform animations in the \
Composable Architecture just as you would in regular SwiftUI.

To animate the changes made to state when an action is sent to the store you can pass along an \
explicit animation, as well, or you can call `viewStore.send` in a `withAnimation` block.
To animate the changes made to state when an action is sent to the store, you can also pass \
along an explicit animation, or you can call `store.send` in a `withAnimation` block.

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

To animate asynchronous changes made to state via effects, use `Effect.run` style of effects \
which allows you to send actions with animations.
To animate asynchronous changes made to state via effects, use the `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.
Try out the demo by tapping or dragging anywhere on the screen to move the dot, and by flipping \
the toggle at the bottom of the screen.
"""

// MARK: - Feature domain

@Reducer
struct Animations {
@ObservableState
Expand Down Expand Up @@ -99,12 +98,8 @@ struct Animations {
}
}

// MARK: - Feature view

struct AnimationsView: View {
@Bindable var store = Store(initialState: Animations.State()) {
Animations()
}
@Bindable var store: StoreOf<Animations>

var body: some View {
VStack(alignment: .leading) {
Expand Down Expand Up @@ -151,27 +146,23 @@ struct AnimationsView: View {
}
}

// MARK: - SwiftUI previews

struct AnimationsView_Previews: PreviewProvider {
static var previews: some View {
Group {
NavigationView {
AnimationsView(
store: Store(initialState: Animations.State()) {
Animations()
}
)
#Preview {
NavigationStack {
AnimationsView(
store: Store(initialState: Animations.State()) {
Animations()
}
)
}
}

NavigationView {
AnimationsView(
store: Store(initialState: Animations.State()) {
Animations()
}
)
#Preview("Dark mode") {
NavigationStack {
AnimationsView(
store: Store(initialState: Animations.State()) {
Animations()
}
.environment(\.colorScheme, .dark)
}
)
}
.environment(\.colorScheme, .dark)
}