Skip to content

Commit 8631b5f

Browse files
SyncUp tutorial fixes (#3139)
* Fixes for TestingSyncUpForm * Typo, heirarchy -> hierarchy * Typo: recursive -> recursively * Typo: comes -> combines * Further fixes for TestingSyncUpForm * Typo: is -> to be * Typo: add -> we added * Typo: 3 -> 2 * Adding a note to avoid confusion * Add more detail to step * Remove stray whitespace causing a diff to incorrectly display * Removed step documentation for unused code * Typo: apart -> a part * Fix App.Path Equatable conformance * App -> AppReducer to avoid conflict with SwiftUI.App * Fix code file link * Typo: reducer -> reducers * Add missing mock * AppReducer -> AppFeature * Standardize tutorial on `@Shared(.syncUps)` * Fix mainactor placement * Fix transcript param not compiling * Add durationPerAttendee to be usable * Add a minimal dismiss override to fit with the docs * Fix commenting out of Tagged discussion * Fix commenting out of TestingSyncUpDetail discussion * Undo a whitespace change * Partial revert of TestingSyncUpForm-02-code-0004.swift * Undo unintentionally committed code * Revert "Add missing mock" This reverts commit 9672ecb. * Revert "Fixes for TestingSyncUpForm" This reverts commit 5bece14. * wip --------- Co-authored-by: Stephen Celis <[email protected]>
1 parent 7461262 commit 8631b5f

31 files changed

+150
-69
lines changed

Sources/ComposableArchitecture/Documentation.docc/Articles/Testing.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ class TimerTests: XCTestCase {
246246
With the basics set up, we can send an action into the system to assert on what happens, such as the
247247
`.startTimerButtonTapped` action. This time we don't actually expect state to change at first
248248
because when starting the timer we don't change state, and so in this case we can leave off the
249-
trailer closure:
249+
trailing closure:
250250

251251
```swift
252252
await store.send(.startTimerButtonTapped)

Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/02-ListsOfSyncUps/ListsOfSyncUps-03-code-0002.diff

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ received action:
1212
_path: [:],
1313
_syncUpsList: SyncUpsList.State(
1414
_destination: nil,
15-
_syncUps: #1 IdentifiedArray(
15+
_syncUps: [
1616
- SyncUp(
1717
- id: Tagged(rawValue: UUID(BCD45D06-2291-4A22-906A-C1EF07500AB7)),
1818
- attendees: [
@@ -34,6 +34,6 @@ received action:
3434
- theme: .bubblegum,
3535
- title: "Point-Free Morning Sync"
3636
- )
37-
)
37+
]
3838
)
3939
)

Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/03-SyncUpForm/SyncUpForm-03-code-0003.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,12 @@ struct SyncUpForm {
3939
guard
4040
!state.syncUp.attendees.isEmpty,
4141
let firstIndex = indices.first
42-
else { return .none }
42+
else {
43+
state.syncUp.attendees.append(
44+
Attendee(id: Attendee.ID())
45+
)
46+
return .none
47+
}
4348
let index = min(firstIndex, state.syncUp.attendees.count - 1)
4449
state.focus = .attendee(state.syncUp.attendees[index].id)
4550
return .none

Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/03-SyncUpForm/TestingSyncUpForm-02-code-0004-previous.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,12 @@ struct SyncUpForm {
3939
guard
4040
!state.syncUp.attendees.isEmpty,
4141
let firstIndex = indices.first
42-
else { return .none }
42+
else {
43+
state.syncUp.attendees.append(
44+
Attendee(id: Attendee.ID())
45+
)
46+
return .none
47+
}
4348
let index = min(firstIndex, state.syncUp.attendees.count - 1)
4449
state.focus = .attendee(state.syncUp.attendees[index].id)
4550
return .none

Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/03-SyncUpForm/TestingSyncUpForm-02-code-0004.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,12 @@ struct SyncUpForm {
4141
guard
4242
!state.syncUp.attendees.isEmpty,
4343
let firstIndex = indices.first
44-
else { return .none }
44+
else {
45+
state.syncUp.attendees.append(
46+
Attendee(id: Attendee.ID())
47+
)
48+
return .none
49+
}
4550
let index = min(firstIndex, state.syncUp.attendees.count - 1)
4651
state.focus = .attendee(state.syncUp.attendees[index].id)
4752
return .none

Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/03-SyncUpForm/TestingSyncUpForm-02-code-0005.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,12 @@ struct SyncUpForm {
4141
guard
4242
!state.syncUp.attendees.isEmpty,
4343
let firstIndex = indices.first
44-
else { return .none }
44+
else {
45+
state.syncUp.attendees.append(
46+
Attendee(id: uuid())
47+
)
48+
return .none
49+
}
4550
let index = min(firstIndex, state.syncUp.attendees.count - 1)
4651
state.focus = .attendee(state.syncUp.attendees[index].id)
4752
return .none
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,62 @@
11
import ComposableArchitecture
22
import SwiftUI
33

4-
@main
5-
struct SyncUpsApp: App {
6-
@MainActor
7-
static let store = Store(initialState: SyncUpsList.State()) {
8-
SyncUpsList()
4+
@Reducer
5+
struct SyncUpsList {
6+
@ObservableState
7+
struct State: Equatable {
8+
@Presents var addSyncUp: SyncUpForm.State?
9+
@Shared(.syncUps) var syncUps
910
}
11+
enum Action {
12+
case addSyncUpButtonTapped
13+
case addSyncUp(PresentationAction<SyncUpForm.Action>)
14+
case confirmAddButtonTapped
15+
case discardButtonTapped
16+
case onDelete(IndexSet)
17+
case syncUpTapped(id: SyncUp.ID)
18+
}
19+
var body: some ReducerOf<Self> {
20+
Reduce { state, action in
21+
switch action {
22+
case .addSyncUpButtonTapped:
23+
state.addSyncUp = SyncUpForm.State(syncUp: SyncUp(id: SyncUp.ID()))
24+
return .none
25+
26+
case .addSyncUp:
27+
return .none
28+
29+
case .confirmAddButtonTapped:
30+
guard let newSyncUp = state.addSyncUp?.syncUp
31+
else { return .none }
32+
state.addSyncUp = nil
33+
state.syncUps.append(newSyncUp)
34+
return .none
35+
36+
case .discardButtonTapped:
37+
state.addSyncUp = nil
38+
return .none
39+
40+
case let .onDelete(indexSet):
41+
state.syncUps.remove(atOffsets: indexSet)
42+
return .none
1043

11-
var body: some Scene {
12-
WindowGroup {
13-
NavigationStack {
14-
SyncUpsListView(store: Self.store)
44+
case .syncUpTapped:
45+
return .none
1546
}
1647
}
48+
.ifLet(\.$addSyncUp, action: \.addSyncUp) {
49+
SyncUpForm()
50+
}
51+
}
52+
}
53+
54+
extension PersistenceReaderKey
55+
where Self == PersistenceKeyDefault<FileStorageKey<IdentifiedArrayOf<SyncUp>>> {
56+
static var syncUps: Self {
57+
PersistenceKeyDefault(
58+
.fileStorage(.documentsDirectory.appending(component: "sync-ups.json")),
59+
[]
60+
)
1761
}
1862
}

Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/05-PersistingSyncUps/PersistingSyncUps.tutorial

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,22 @@
4545
bottom of the file to define such a URL, and then provide it to the `.fileStorage` value.
4646

4747
@Code(name: "SyncUpsList.swift", file: PersistingSyncUps-01-code-0003.swift)
48-
49-
> Note: It is possible to provide some type safety between the URL provided and the expected
50-
> type of data to be stored on disk. See
51-
> [type-safe keys](<doc:SharingState#Type-safe-keys>) for more information.
5248
}
53-
49+
5450
With that change the project should be compiling. It is worth noting that
5551
``ComposableArchitecture/PersistenceReaderKey/fileStorage(_:)`` only works with `Codable` data
5652
types, and earlier in the tutorial when we added models to Models.swift we made them codable from
5753
the beginning.
58-
54+
55+
@Step {
56+
Before moving on, we can still make this better.
57+
It is possible to provide some type safety between the URL provided and the expected
58+
type of data to be stored on disk, and make usage more concise. See
59+
[type-safe keys](<doc:SharingState#Type-safe-keys>) for more information.
60+
61+
@Code(name: "SyncUpsList.swift", file: PersistingSyncUps-01-code-0004.swift)
62+
}
63+
5964
To confirm that persistence works we need to run the app in the simulator, but we haven't done
6065
that yet in this tutorial. To do that we need to update the entry point of the app to use the
6166
`SyncUpsListView`.
@@ -67,14 +72,14 @@
6772
> Note: We construct the ``ComposableArchitecture/Store`` as a static so that it is
6873
initialized only one time, and so that it is not created while running Xcode previews.
6974

70-
@Code(name: "SyncUpsApp.swift", file: PersistingSyncUps-01-code-0004.swift)
75+
@Code(name: "SyncUpsApp.swift", file: PersistingSyncUps-01-code-0005.swift)
7176
}
7277

7378
@Step {
7479
Run the app in the simulator, add a sync-up, and then relaunch the application to see that
7580
it restores the previously created sync-up. This shows that data is persisting.
7681

77-
@Video(source: PersistingSyncUps-01-video-0005.mov)
82+
@Video(source: PersistingSyncUps-01-video-0006.mov)
7883
}
7984
}
8085
}

Sources/ComposableArchitecture/Documentation.docc/Tutorials/BuildingSyncUps/07-SyncUpDetailNavigation/SyncUpDetailNavigation-01-code-0001.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import ComposableArchitecture
22
import SwiftUI
33

44
@Reducer
5-
struct App {
5+
struct AppFeature {
66
@ObservableState
77
struct State: Equatable {
88
}

0 commit comments

Comments
 (0)