diff --git a/Sources/ComposableArchitecture/Documentation.docc/Articles/Testing.md b/Sources/ComposableArchitecture/Documentation.docc/Articles/Testing.md index 691dd7a67d4d..cf3962390bd0 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Articles/Testing.md +++ b/Sources/ComposableArchitecture/Documentation.docc/Articles/Testing.md @@ -246,7 +246,7 @@ class TimerTests: XCTestCase { With the basics set up, we can send an action into the system to assert on what happens, such as the `.startTimerButtonTapped` action. This time we don't actually expect state to change at first because when starting the timer we don't change state, and so in this case we can leave off the -trailer closure: +trailing closure: ```swift await store.send(.startTimerButtonTapped) diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/02-ListsOfSyncUps/ListsOfSyncUps-03-code-0002.diff b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/02-ListsOfSyncUps/ListsOfSyncUps-03-code-0002.diff index b6b24374e31d..f828c8264539 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/02-ListsOfSyncUps/ListsOfSyncUps-03-code-0002.diff +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/02-ListsOfSyncUps/ListsOfSyncUps-03-code-0002.diff @@ -12,7 +12,7 @@ received action: _path: [:], _syncUpsList: SyncUpsList.State( _destination: nil, - _syncUps: #1 IdentifiedArray( + _syncUps: [ - SyncUp( - id: Tagged(rawValue: UUID(BCD45D06-2291-4A22-906A-C1EF07500AB7)), - attendees: [ @@ -34,6 +34,6 @@ received action: - theme: .bubblegum, - title: "Point-Free Morning Sync" - ) - ) + ] ) ) diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/03-SyncUpForm/SyncUpForm-03-code-0003.swift b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/03-SyncUpForm/SyncUpForm-03-code-0003.swift index afa2546bd4b7..0c04bb9d8d6c 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/03-SyncUpForm/SyncUpForm-03-code-0003.swift +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/03-SyncUpForm/SyncUpForm-03-code-0003.swift @@ -39,7 +39,12 @@ struct SyncUpForm { guard !state.syncUp.attendees.isEmpty, let firstIndex = indices.first - else { return .none } + else { + state.syncUp.attendees.append( + Attendee(id: Attendee.ID()) + ) + return .none + } let index = min(firstIndex, state.syncUp.attendees.count - 1) state.focus = .attendee(state.syncUp.attendees[index].id) return .none diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/03-SyncUpForm/TestingSyncUpForm-02-code-0004-previous.swift b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/03-SyncUpForm/TestingSyncUpForm-02-code-0004-previous.swift index afa2546bd4b7..0c04bb9d8d6c 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/03-SyncUpForm/TestingSyncUpForm-02-code-0004-previous.swift +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/03-SyncUpForm/TestingSyncUpForm-02-code-0004-previous.swift @@ -39,7 +39,12 @@ struct SyncUpForm { guard !state.syncUp.attendees.isEmpty, let firstIndex = indices.first - else { return .none } + else { + state.syncUp.attendees.append( + Attendee(id: Attendee.ID()) + ) + return .none + } let index = min(firstIndex, state.syncUp.attendees.count - 1) state.focus = .attendee(state.syncUp.attendees[index].id) return .none diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/03-SyncUpForm/TestingSyncUpForm-02-code-0004.swift b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/03-SyncUpForm/TestingSyncUpForm-02-code-0004.swift index afa49b2b7982..6d0175a9f05e 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/03-SyncUpForm/TestingSyncUpForm-02-code-0004.swift +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/03-SyncUpForm/TestingSyncUpForm-02-code-0004.swift @@ -41,7 +41,12 @@ struct SyncUpForm { guard !state.syncUp.attendees.isEmpty, let firstIndex = indices.first - else { return .none } + else { + state.syncUp.attendees.append( + Attendee(id: Attendee.ID()) + ) + return .none + } let index = min(firstIndex, state.syncUp.attendees.count - 1) state.focus = .attendee(state.syncUp.attendees[index].id) return .none diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/03-SyncUpForm/TestingSyncUpForm-02-code-0005.swift b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/03-SyncUpForm/TestingSyncUpForm-02-code-0005.swift index aacb6311d6e1..530779b4cb60 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/03-SyncUpForm/TestingSyncUpForm-02-code-0005.swift +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/03-SyncUpForm/TestingSyncUpForm-02-code-0005.swift @@ -41,7 +41,12 @@ struct SyncUpForm { guard !state.syncUp.attendees.isEmpty, let firstIndex = indices.first - else { return .none } + else { + state.syncUp.attendees.append( + Attendee(id: uuid()) + ) + return .none + } let index = min(firstIndex, state.syncUp.attendees.count - 1) state.focus = .attendee(state.syncUp.attendees[index].id) return .none diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/05-PersistingSyncUps/PersistingSyncUps-01-code-0004.swift b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/05-PersistingSyncUps/PersistingSyncUps-01-code-0004.swift index 9c5b87dacfb1..b5cac8dfa36a 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/05-PersistingSyncUps/PersistingSyncUps-01-code-0004.swift +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/05-PersistingSyncUps/PersistingSyncUps-01-code-0004.swift @@ -1,18 +1,62 @@ import ComposableArchitecture import SwiftUI -@main -struct SyncUpsApp: App { - @MainActor - static let store = Store(initialState: SyncUpsList.State()) { - SyncUpsList() +@Reducer +struct SyncUpsList { + @ObservableState + struct State: Equatable { + @Presents var addSyncUp: SyncUpForm.State? + @Shared(.syncUps) var syncUps } + enum Action { + case addSyncUpButtonTapped + case addSyncUp(PresentationAction) + case confirmAddButtonTapped + case discardButtonTapped + case onDelete(IndexSet) + case syncUpTapped(id: SyncUp.ID) + } + var body: some ReducerOf { + Reduce { state, action in + switch action { + case .addSyncUpButtonTapped: + state.addSyncUp = SyncUpForm.State(syncUp: SyncUp(id: SyncUp.ID())) + return .none + + case .addSyncUp: + return .none + + case .confirmAddButtonTapped: + guard let newSyncUp = state.addSyncUp?.syncUp + else { return .none } + state.addSyncUp = nil + state.syncUps.append(newSyncUp) + return .none + + case .discardButtonTapped: + state.addSyncUp = nil + return .none + + case let .onDelete(indexSet): + state.syncUps.remove(atOffsets: indexSet) + return .none - var body: some Scene { - WindowGroup { - NavigationStack { - SyncUpsListView(store: Self.store) + case .syncUpTapped: + return .none } } + .ifLet(\.$addSyncUp, action: \.addSyncUp) { + SyncUpForm() + } + } +} + +extension PersistenceReaderKey +where Self == PersistenceKeyDefault>> { + static var syncUps: Self { + PersistenceKeyDefault( + .fileStorage(.documentsDirectory.appending(component: "sync-ups.json")), + [] + ) } } diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/05-PersistingSyncUps/PersistingSyncUps-01-video-0005.mov b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/05-PersistingSyncUps/PersistingSyncUps-01-video-0006.mov similarity index 100% rename from Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/05-PersistingSyncUps/PersistingSyncUps-01-video-0005.mov rename to Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/05-PersistingSyncUps/PersistingSyncUps-01-video-0006.mov diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/05-PersistingSyncUps/PersistingSyncUps.tutorial b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/05-PersistingSyncUps/PersistingSyncUps.tutorial index 2e7e7b609623..48b72059fa80 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/05-PersistingSyncUps/PersistingSyncUps.tutorial +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/05-PersistingSyncUps/PersistingSyncUps.tutorial @@ -45,17 +45,22 @@ bottom of the file to define such a URL, and then provide it to the `.fileStorage` value. @Code(name: "SyncUpsList.swift", file: PersistingSyncUps-01-code-0003.swift) - - > Note: It is possible to provide some type safety between the URL provided and the expected - > type of data to be stored on disk. See - > [type-safe keys]() for more information. } - + With that change the project should be compiling. It is worth noting that ``ComposableArchitecture/PersistenceReaderKey/fileStorage(_:)`` only works with `Codable` data types, and earlier in the tutorial when we added models to Models.swift we made them codable from the beginning. - + + @Step { + Before moving on, we can still make this better. + It is possible to provide some type safety between the URL provided and the expected + type of data to be stored on disk, and make usage more concise. See + [type-safe keys]() for more information. + + @Code(name: "SyncUpsList.swift", file: PersistingSyncUps-01-code-0004.swift) + } + To confirm that persistence works we need to run the app in the simulator, but we haven't done that yet in this tutorial. To do that we need to update the entry point of the app to use the `SyncUpsListView`. @@ -67,14 +72,14 @@ > Note: We construct the ``ComposableArchitecture/Store`` as a static so that it is initialized only one time, and so that it is not created while running Xcode previews. - @Code(name: "SyncUpsApp.swift", file: PersistingSyncUps-01-code-0004.swift) + @Code(name: "SyncUpsApp.swift", file: PersistingSyncUps-01-code-0005.swift) } @Step { Run the app in the simulator, add a sync-up, and then relaunch the application to see that it restores the previously created sync-up. This shows that data is persisting. - @Video(source: PersistingSyncUps-01-video-0005.mov) + @Video(source: PersistingSyncUps-01-video-0006.mov) } } } diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0001.swift b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0001.swift index 08fc993ea3aa..7dedd8de9364 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0001.swift +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0001.swift @@ -2,7 +2,7 @@ import ComposableArchitecture import SwiftUI @Reducer -struct App { +struct AppFeature { @ObservableState struct State: Equatable { } diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0002.swift b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0002.swift index f4cb3b6ff72e..9842dd99a57b 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0002.swift +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0002.swift @@ -2,7 +2,7 @@ import ComposableArchitecture import SwiftUI @Reducer -struct App { +struct AppFeature { @ObservableState struct State: Equatable { var syncUpsList = SyncUpsList.State() diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0003.swift b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0003.swift index e41cefed80c7..b57ee207ae1a 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0003.swift +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0003.swift @@ -2,7 +2,7 @@ import ComposableArchitecture import SwiftUI @Reducer -struct App { +struct AppFeature { @ObservableState struct State: Equatable { var path = StackState() diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0004.swift b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0004.swift index adc860b69739..adaf32a10130 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0004.swift +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0004.swift @@ -2,7 +2,7 @@ import ComposableArchitecture import SwiftUI @Reducer -struct App { +struct AppFeature { @Reducer enum Path { } diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0005.swift b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0005.swift index 46829e3ac4b4..2617561400fc 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0005.swift +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0005.swift @@ -2,7 +2,7 @@ import ComposableArchitecture import SwiftUI @Reducer -struct App { +struct AppFeature { @Reducer enum Path { case detail(SyncUpDetail) diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0006.swift b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0006.swift index 0674e1a33eaf..b3de0286495b 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0006.swift +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0006.swift @@ -2,8 +2,8 @@ import ComposableArchitecture import SwiftUI @Reducer -struct App { - @Reducer +struct AppFeature { + @Reducer(state: .equatable) enum Path { case detail(SyncUpDetail) } diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0007.swift b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0007.swift index cb877d26cddd..3e3ce424df63 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0007.swift +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0007.swift @@ -2,8 +2,8 @@ import ComposableArchitecture import SwiftUI @Reducer -struct App { - @Reducer +struct AppFeature { + @Reducer(state: .equatable) enum Path { case detail(SyncUpDetail) } diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0008.swift b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0008.swift index 07cbda80fc18..b2d73e715f49 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0008.swift +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0008.swift @@ -2,8 +2,8 @@ import ComposableArchitecture import SwiftUI @Reducer -struct App { - @Reducer +struct AppFeature { + @Reducer(state: .equatable) enum Path { case detail(SyncUpDetail) } diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0001-previous.swift b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0001-previous.swift index 82cb9df5186d..94882b3a5cc6 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0001-previous.swift +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0001-previous.swift @@ -2,6 +2,6 @@ import ComposableArchitecture import SwiftUI @Reducer -struct App { +struct AppFeature { // ... } diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0001.swift b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0001.swift index 3686c18b970f..83b87c743d25 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0001.swift +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0001.swift @@ -2,7 +2,7 @@ import ComposableArchitecture import SwiftUI @Reducer -struct App { +struct AppFeature { // ... } diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0002.swift b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0002.swift index 9f76cf381607..0b2e83e2a6e5 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0002.swift +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0002.swift @@ -2,10 +2,10 @@ import ComposableArchitecture import SwiftUI @Reducer -struct App { +struct AppFeature { // ... } struct AppView: View { - @Bindable var store: StoreOf + @Bindable var store: StoreOf } diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0003.swift b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0003.swift index 6890db085ea4..93bb80b90aa5 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0003.swift +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0003.swift @@ -2,12 +2,12 @@ import ComposableArchitecture import SwiftUI @Reducer -struct App { +struct AppFeature { // ... } struct AppView: View { - @Bindable var store: StoreOf + @Bindable var store: StoreOf var body: some View { NavigationStack( diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0004.swift b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0004.swift index 6abf032b7d69..afdb44ff0782 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0004.swift +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0004.swift @@ -2,12 +2,12 @@ import ComposableArchitecture import SwiftUI @Reducer -struct App { +struct AppFeature { // ... } struct AppView: View { - @Bindable var store: StoreOf + @Bindable var store: StoreOf var body: some View { NavigationStack( diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0005.swift b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0005.swift index 802a79217fdc..2fcd391982e7 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0005.swift +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0005.swift @@ -2,12 +2,12 @@ import ComposableArchitecture import SwiftUI @Reducer -struct App { +struct AppFeature { // ... } struct AppView: View { - @Bindable var store: StoreOf + @Bindable var store: StoreOf var body: some View { NavigationStack( diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0006.swift b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0006.swift index d662c5f0b816..58d7181ab0e6 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0006.swift +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0006.swift @@ -2,12 +2,12 @@ import ComposableArchitecture import SwiftUI @Reducer -struct App { +struct AppFeature { // ... } struct AppView: View { - @Bindable var store: StoreOf + @Bindable var store: StoreOf var body: some View { NavigationStack( diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0007.swift b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0007.swift index 9554ffcbf242..40fb5c5e8c34 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0007.swift +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-02-code-0007.swift @@ -2,12 +2,12 @@ import ComposableArchitecture import SwiftUI @Reducer -struct App { +struct AppFeature { // ... } struct AppView: View { - @Bindable var store: StoreOf + @Bindable var store: StoreOf var body: some View { NavigationStack( @@ -28,11 +28,11 @@ struct AppView: View { #Preview { AppView( store: Store( - initialState: App.State( + initialState: AppFeature.State( syncUpsList: SyncUpsList.State() ) ) { - App() + AppFeature() } ) } diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-03-code-0002.swift b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-03-code-0002.swift index 884a317a1d9b..7bbecd9beb37 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-03-code-0002.swift +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-03-code-0002.swift @@ -13,7 +13,7 @@ struct SyncUpsListView: View { List { ForEach(store.syncUps) { syncUp in NavigationLink( - state: App.Path.State.detail(SyncUpDetail.State(syncUp: <#Shared#>)) + state: AppFeature.Path.State.detail(SyncUpDetail.State(syncUp: <#Shared#>)) ) { CardView(syncUp: syncUp) } diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-03-code-0003.swift b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-03-code-0003.swift index b7daca3b17ec..c9d7ebed5381 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-03-code-0003.swift +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-03-code-0003.swift @@ -13,7 +13,7 @@ struct SyncUpsListView: View { List { ForEach(store.$syncUps.elements) { $syncUp in NavigationLink( - state: App.Path.State.detail(SyncUpDetail.State(syncUp: <#Shared#>)) + state: AppFeature.Path.State.detail(SyncUpDetail.State(syncUp: <#Shared#>)) ) { CardView(syncUp: syncUp) } diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-03-code-0004.swift b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-03-code-0004.swift index 02a933c29ea8..0ab0096da0e1 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-03-code-0004.swift +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-03-code-0004.swift @@ -13,7 +13,7 @@ struct SyncUpsListView: View { List { ForEach(store.$syncUps.elements) { $syncUp in NavigationLink( - state: App.Path.State.detail(SyncUpDetail.State(syncUp: $syncUp)) + state: AppFeature.Path.State.detail(SyncUpDetail.State(syncUp: $syncUp)) ) { CardView(syncUp: syncUp) } diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation.tutorial b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation.tutorial index ee7d377e742f..6b09a6ac6bf6 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation.tutorial +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation.tutorial @@ -18,7 +18,12 @@ > Note: You may already have an App.swift in your project that holds the entry point of the application (i.e. `@main`). If so, you can name this file AppFeature.swift, or you can rename the entry point to Main.swift. - + > + > We often leave out suffixing reducers with names like "feature" or "reducer", but in this + > case we must avoid conflicting with SwiftUI's + > [App](https://developer.apple.com/documentation/swiftui/app) protocol, so we'll use + > `AppFeature`, instead. + @Code(name: "App.swift", file: SyncUpDetailNavigation-01-code-0001.swift) } @@ -31,23 +36,23 @@ The sync-ups list feature is a little different from all of those other features, though. It is at the root of the stack, and it can not be dismissed. For that reason it is composed into the `AppFeature` by simply holding onto its state and actions, and using the - ``ComposableArchitecture/Scope`` reducer to compose it into `App`'s `body`. + ``ComposableArchitecture/Scope`` reducer to compose it into `AppFeature`'s `body`. @Step { - Integrate the `SyncUpsList` reducer into the `AppReducer` by holding onto its state and + Integrate the `SyncUpsList` reducer into the `AppFeature` by holding onto its state and actions, and using the ``ComposableArchitecture/Scope`` reducer to compose the two reducers together. @Code(name: "App.swift", file: SyncUpDetailNavigation-01-code-0002.swift) } - Next we want to integrate the `SyncUpDetail` reducer into the `AppReducer`. The library comes + Next we want to integrate the `SyncUpDetail` reducer into the `AppFeature`. The library comes with specific tools to do this for navigation stacks. It starts by using ``ComposableArchitecture/StackState``, which is a collection type provided by the library specifically tuned for dealing with navigation stacks. @Step { - Integrate the state of `SyncUpDetail` in `App` by holding onto + Integrate the state of `SyncUpDetail` in `AppFeature` by holding onto ``ComposableArchitecture/StackState``. We like to call this variable `path` since it mimics the `NavigationStack(path:)` API in SwiftUI. @@ -63,7 +68,7 @@ in . @Step { - Create a new `Path` reducer inside `App`. Like the `Destination` reducer we defined for the + Create a new `Path` reducer inside `AppFeature`. Like the `Destination` reducer we defined for the sync-up detail screen, `Path` type will be an _enum_ so that it can compose all of the screens you can navigate to into a single reducer. @@ -81,17 +86,23 @@ of each screen in the navigation stack. In the future as we add more screens to the stack we will just add more cases to this reducer. - Next we will integrate the `Path` reducer into the `App` reducer. + Next we will integrate the `Path` reducer into the `AppFeature` reducer. @Step { Update the `path` variable to hold onto ``ComposableArchitecture/StackState`` of `Path.State`. + + > Important: In order to maintain `State`'s `Equatable`, we must conform `Path` to be + > `Equatable` as well. While this would typically be done with an extension, a Swift + > compiler bug prevents this, so the ``ComposableArchitecture/Reducer(state:action:)`` macro + > can generate this conformance for you by specifying the `state` parameter. See the article + > for more. - @Code(name: "App.swift", file: SyncUpDetailNavigation-01-code-0006.swift, previousFile: SyncUpDetailNavigation-01-code-0006-previous.swift) + @Code(name: "App.swift", file: SyncUpDetailNavigation-01-code-0006.swift) } @Step { - Integrate the `Path`'s actions into `App` by adding a case to its `Action` enum, and using + Integrate the `Path`'s actions into `AppFeature` by adding a case to its `Action` enum, and using the ``ComposableArchitecture/StackAction``. We can use the ``ComposableArchitecture/StackActionOf`` type alias to specify a single generic of the path reducer. @@ -109,7 +120,7 @@ pop an element from the stack (``ComposableArchitecture/StackAction/popFrom(id:)``). @Step { - Integrate the `Path` reducer into the `body` of the `App` reducer by using the + Integrate the `Path` reducer into the `body` of the `AppFeature` reducer by using the ``ComposableArchitecture/Reducer/forEach(_:action:)`` operator. @Code(name: "App.swift", file: SyncUpDetailNavigation-01-code-0008.swift) @@ -120,7 +131,7 @@ it to the current reducer in the stack, and automatically cancelling effects when a feature is popped from the stack. - That is all it takes to create the `App` reducer and integrate the `SyncUpDetail` into the + That is all it takes to create the `AppFeature` reducer and integrate the `SyncUpDetail` into the stack. Next We will create the view layer. } } @@ -184,7 +195,7 @@ @Step { Add a preview to the bottom of the file. - @Code(name: "App.swift", file: SyncUpDetailNavigation-02-code-0006.swift) + @Code(name: "App.swift", file: SyncUpDetailNavigation-02-code-0007.swift) } That's all it takes to build the root `AppView` that holds onto a `NavigationStack`. But @@ -211,7 +222,7 @@ } This special initializer takes a piece of state that matches the type that is held in the - root feature's ``ComposableArchitecture/StackState``. In our case, that is `App.Path.State`, + root feature's ``ComposableArchitecture/StackState``. In our case, that is `AppFeature.Path.State`, which is an enum of all the possible destinations that can be navigated to. @Step { @@ -248,7 +259,7 @@ state to the path, thus triggering a drill-down animation. This is the easiest way to navigate to one screen to another, but it does have some drawbacks. - Because we need to reference all of `App.State.Path` to construct a navigation link, it can + Because we need to reference all of `AppFeature.State.Path` to construct a navigation link, it can make modularization difficult. Every navigation link will need access to every feature's state. diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/08-RecordMeeting/ImplementingTimer-04-code-0016.swift b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/08-RecordMeeting/ImplementingTimer-04-code-0016.swift index 2a2a69b384ca..d9bffcd08ba7 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/08-RecordMeeting/ImplementingTimer-04-code-0016.swift +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/08-RecordMeeting/ImplementingTimer-04-code-0016.swift @@ -24,6 +24,7 @@ final class RecordMeetingTests: XCTestCase { $0.continuousClock = clock $0.date.now = Date(timeIntervalSince1970: 1234567890) $0.uuid = .incrementing + $0.dismiss = DismissEffect {} } let onAppearTask = await store.send(.onAppear) diff --git a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/08-RecordMeeting/ImplementingTimer.tutorial b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/08-RecordMeeting/ImplementingTimer.tutorial index 2de3b7747fb7..539d398e4f48 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/08-RecordMeeting/ImplementingTimer.tutorial +++ b/Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/08-RecordMeeting/ImplementingTimer.tutorial @@ -475,7 +475,7 @@ To fix the test we need to override the ``Dependencies/DependencyValues/dismiss`` dependency. @Step { - Override the `dismiss` dependency in the `withDependencies` trailer closure. + Override the `dismiss` dependency in the `withDependencies` trailing closure. @Code(name: "RecordMeetingTests.swift", file: ImplementingTimer-04-code-0016.swift) }