-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New tutorial: Building SyncUps. (#3039)
* New tutorial: Building SyncUps. * fix * clean up * wip * wip * wip * wip * wip --------- Co-authored-by: Stephen Celis <stephen@stephencelis.com>
- Loading branch information
1 parent
4c36b18
commit 307c851
Showing
322 changed files
with
21,974 additions
and
0 deletions.
There are no files selected for viewing
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
Binary file added
BIN
+421 KB
...on.docc/Tutorials/BuildingSyncUps/01-WhatIsSyncUps/CreateProject-0001-image.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+493 KB
...on.docc/Tutorials/BuildingSyncUps/01-WhatIsSyncUps/CreateProject-0002-image.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+765 KB
...on.docc/Tutorials/BuildingSyncUps/01-WhatIsSyncUps/CreateProject-0003-image.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+500 KB
...on.docc/Tutorials/BuildingSyncUps/01-WhatIsSyncUps/CreateProject-0004-image.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+85.5 KB
...on.docc/Tutorials/BuildingSyncUps/01-WhatIsSyncUps/TourOfSyncUps-0003-image.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+60.5 KB
...on.docc/Tutorials/BuildingSyncUps/01-WhatIsSyncUps/TourOfSyncUps-0004-image.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+39.4 KB
...on.docc/Tutorials/BuildingSyncUps/01-WhatIsSyncUps/TourOfSyncUps-0005-image.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+70.7 KB
...on.docc/Tutorials/BuildingSyncUps/01-WhatIsSyncUps/TourOfSyncUps-0006-image.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+146 KB
...on.docc/Tutorials/BuildingSyncUps/01-WhatIsSyncUps/TourOfSyncUps-0007-image.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+392 KB
...on.docc/Tutorials/BuildingSyncUps/01-WhatIsSyncUps/TourOfSyncUps-0008-image.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+174 KB
...on.docc/Tutorials/BuildingSyncUps/01-WhatIsSyncUps/TourOfSyncUps-0009-image.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+353 KB
...on.docc/Tutorials/BuildingSyncUps/01-WhatIsSyncUps/TourOfSyncUps-0010-image.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+178 KB
...on.docc/Tutorials/BuildingSyncUps/01-WhatIsSyncUps/TourOfSyncUps-0011-image.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+117 KB
...on.docc/Tutorials/BuildingSyncUps/01-WhatIsSyncUps/TourOfSyncUps-0012-image.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+409 KB
...on.docc/Tutorials/BuildingSyncUps/01-WhatIsSyncUps/TourOfSyncUps-0013-image.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+85.5 KB
...on.docc/Tutorials/BuildingSyncUps/01-WhatIsSyncUps/TourOfSyncUps-0014-image.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
183 changes: 183 additions & 0 deletions
183
...ture/Documentation.docc/Tutorials/BuildingSyncUps/01-WhatIsSyncUps/WhatIsSyncUps.tutorial
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
@Tutorial(time: 5) { | ||
@Intro(title: "What is SyncUps?") { | ||
Let's start with a tour of the application we will be building. It's called SyncUps, and it's a | ||
recreation of Apple's Scrumdinger demo application. | ||
|
||
It features multiple different navigation patterns, has interesting side effects such as a timer | ||
and speech recognizer, and it's complex enough that it warrants a test suite to be confident it | ||
works the way we expect. | ||
} | ||
|
||
@Section(title: "A tour of SyncUps") { | ||
@ContentAndMedia { | ||
This very repo contains a [demo project][syncups] that shows how to build a moderately complex | ||
application using the Composable Architecture. Let's go on a quick tour of the app so that we | ||
can see what exactly we will be building in this tutorial. | ||
|
||
[syncups]: https://github.com/pointfreeco/swift-composable-architecture/tree/main/Examples/SyncUps | ||
} | ||
|
||
@Steps { | ||
@Step { | ||
Start by cloning the Composable Architecture repo: | ||
|
||
`git clone https://www.github.com/pointfreeco/swift-composable-architecture` | ||
} | ||
@Step { | ||
Navigate to the Examples/SyncUps directory and open the SyncUps.xcodeproj file. | ||
} | ||
@Step { | ||
Press ⌘R to run the application in the simulator. | ||
|
||
> Note: You may need to enable macros that the library uses before you can run the app in | ||
> the simulator. | ||
|
||
@Image(source: TourOfSyncUps-0003-image.png) | ||
} | ||
@Step { | ||
Tap the "+" button in the top-right to bring up a sheet where you can enter the details of a | ||
new sync-up. | ||
|
||
@Image(source: TourOfSyncUps-0004-image.png) | ||
} | ||
@Step { | ||
Tap the "Add" button in the top-right to complete adding the sync-up. Notice that the sheet | ||
dismisses and the new sync-up is added to the list. | ||
|
||
@Image(source: TourOfSyncUps-0005-image.png) | ||
} | ||
@Step { | ||
Tap on the row in the list in order to drill down to the details of the sync-up. | ||
|
||
@Image(source: TourOfSyncUps-0006-image.png) | ||
} | ||
|
||
From here there are a few actions you can perform. You can edit the sync-up's details, you can | ||
record a new meeting in the sync-up, you can view past meetings (currently there aren't any), | ||
and you can delete the sync-up. | ||
|
||
@Step { | ||
Tap the "Edit" button in the top-right to bring up a sheet with all the info of the sync-up. | ||
Edit the sync-up by adding a new attendee, "Blob Esq", and tap "Done" in the top-right to | ||
commit that change. Notice that the sheet is dismissed and the change was applied to the | ||
form in the detail screen. | ||
|
||
@Image(source: TourOfSyncUps-0007-image.png) | ||
} | ||
|
||
@Step { | ||
Tap the "Start meeting" button to start a meeting. Notice that you drill down to a new | ||
screen and are immediately prompted asking for speech recognition permission. | ||
|
||
@Image(source: TourOfSyncUps-0008-image.png) | ||
} | ||
|
||
@Step { | ||
Either grant access or deny access in order to officially start the meeting. Once done you | ||
will see that a timer begins and will automatically control which speaker is currently | ||
active. | ||
|
||
> Note: You can choose to grant or deny, but currently the speech recognition APIs do not | ||
> work in simulators and so it doesn't really matter which you choose. | ||
|
||
@Image(source: TourOfSyncUps-0009-image.png) | ||
} | ||
|
||
The meeting will automatically end once the time runs out, but there is a faster way to end | ||
the meeting too. | ||
|
||
@Step { | ||
Tap the "End meeting" to bring up an alert showing your options for ending the meeting | ||
early. | ||
|
||
@Image(source: TourOfSyncUps-0010-image.png) | ||
} | ||
|
||
You can either choose to end the meeting early by saving the meeting in the history or | ||
discarding the meeting, or you can cancel and go back to the meeting. | ||
|
||
@Step { | ||
Tap "Save and end" in order to end the meeting early. Notice that you are popped back to | ||
the detail screen, and that the "Past meetings" section has a new row for the meeting | ||
that was just recorded. | ||
|
||
@Image(source: TourOfSyncUps-0011-image.png) | ||
} | ||
|
||
@Step { | ||
Tap the newly added meeting in order to drill down to the meeting details. | ||
|
||
> Note: Currently there is no transcript recorded for this meeting because the speech | ||
> recognition APIs do not work in the simulator. | ||
|
||
@Image(source: TourOfSyncUps-0012-image.png) | ||
} | ||
|
||
@Step { | ||
Go back to the detail of the sync-up and tap the "Delete" button to see an alert asking you | ||
to confirm that you want to delete the sync-up. | ||
|
||
@Image(source: TourOfSyncUps-0013-image.png) | ||
} | ||
|
||
@Step { | ||
Tap "Yes" to confirm deleting the sync-up. You will be popped back to the root list of | ||
sync-ups, which will be empty now that the sync-up was deleted. | ||
|
||
@Image(source: TourOfSyncUps-0014-image.png) | ||
} | ||
|
||
That completes the tour! Another feature of the app that was working behind the scenes without | ||
you noticing was automatic persistence of the data. Every change made to the sync-up, from | ||
adding a new attendee to recording a new meeting, was automatically persisted to a JSON file | ||
on disk. This means that when you quit the app and re-open it will have all of your data | ||
immediately loaded from disk. | ||
} | ||
} | ||
|
||
@Section(title: "Create the SyncUps project") { | ||
@ContentAndMedia { | ||
Let's create a brand new project so that we can rebuild the SyncUps app from scratch. This | ||
will give us the opportunity to explore how to solve various problems with the Composable | ||
Architecture, such as domain modeling, navigation, state sharing, persistence, effects, | ||
testing, and more! | ||
} | ||
|
||
@Steps { | ||
@Step { | ||
Start by creating a brand new Xcode project. For the purpose of this tutorial we will target | ||
the newest version of iOS (17.4 at the time of writing this), but do know that it is | ||
possible to build apps with the Composable Architecture that target older versions of | ||
iOS (see <doc:ObservationBackport> for more information on this). | ||
|
||
@Image(source: CreateProject-0001-image.png) | ||
} | ||
|
||
@Step { | ||
Import the Composable Architecture. Currently your project does not depend on the Composable | ||
Architecture and so it will not compile yet. | ||
|
||
@Image(source: CreateProject-0002-image.png) | ||
} | ||
|
||
@Step { | ||
Click on the "Search..." button to search the Swift Package Index for our library. Add | ||
the library to your project. Be sure to target the newest version of the library (at least | ||
1.10.0). | ||
|
||
@Image(source: CreateProject-0003-image.png) | ||
} | ||
|
||
@Step { | ||
Navigate to the project settings and turn "Strict Concurrency Checking" to "Complete". This | ||
will force us to handle any potentially problematic concurrency code as we progress through | ||
the tutorial. | ||
|
||
@Image(source: CreateProject-0004-image) | ||
} | ||
|
||
That is all it takes to set up the project for our SyncUps recreation. We can now start | ||
building our first feature in <doc:ListsOfSyncUps>. | ||
} | ||
} | ||
} |
1 change: 1 addition & 0 deletions
1
...tation.docc/Tutorials/BuildingSyncUps/02-ListsOfSyncUps/ListsOfSyncUps-01-code-0001.swift
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
import Foundation |
57 changes: 57 additions & 0 deletions
57
...tation.docc/Tutorials/BuildingSyncUps/02-ListsOfSyncUps/ListsOfSyncUps-01-code-0002.swift
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import Foundation | ||
import SwiftUI | ||
|
||
struct SyncUp: Equatable, Identifiable, Codable { | ||
let id: UUID | ||
var attendees: [Attendee] = [] | ||
var duration: Duration = .seconds(60 * 5) | ||
var meetings: [Meeting] = [] | ||
var theme: Theme = .bubblegum | ||
var title = "" | ||
} | ||
|
||
struct Attendee: Equatable, Identifiable, Codable { | ||
let id: UUID | ||
var name = "" | ||
} | ||
|
||
struct Meeting: Equatable, Identifiable, Codable { | ||
let id: UUID | ||
let date: Date | ||
var transcript: String | ||
} | ||
|
||
enum Theme: String, CaseIterable, Equatable, Identifiable, Codable { | ||
var id: Self { self } | ||
|
||
case bubblegum | ||
case buttercup | ||
case indigo | ||
case lavender | ||
case magenta | ||
case navy | ||
case orange | ||
case oxblood | ||
case periwinkle | ||
case poppy | ||
case purple | ||
case seafoam | ||
case sky | ||
case tan | ||
case teal | ||
case yellow | ||
|
||
var accentColor: Color { | ||
switch self { | ||
case .bubblegum, .buttercup, .lavender, .orange, .periwinkle, .poppy, .seafoam, .sky, .tan, | ||
.teal, .yellow: | ||
return .black | ||
case .indigo, .magenta, .navy, .oxblood, .purple: | ||
return .white | ||
} | ||
} | ||
|
||
var mainColor: Color { Color(rawValue) } | ||
|
||
var name: String { rawValue.capitalized } | ||
} |
58 changes: 58 additions & 0 deletions
58
...tation.docc/Tutorials/BuildingSyncUps/02-ListsOfSyncUps/ListsOfSyncUps-01-code-0003.swift
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import Foundation | ||
import IdentifiedCollections | ||
import SwiftUI | ||
|
||
struct SyncUp: Equatable, Identifiable, Codable { | ||
let id: UUID | ||
var attendees: IdentifiedArrayOf<Attendee> = [] | ||
var duration: Duration = .seconds(60 * 5) | ||
var meetings: IdentifiedArrayOf<Meeting> = [] | ||
var theme: Theme = .bubblegum | ||
var title = "" | ||
} | ||
|
||
struct Attendee: Equatable, Identifiable, Codable { | ||
let id: UUID | ||
var name = "" | ||
} | ||
|
||
struct Meeting: Equatable, Identifiable, Codable { | ||
let id: UUID | ||
let date: Date | ||
var transcript: String | ||
} | ||
|
||
enum Theme: String, CaseIterable, Equatable, Identifiable, Codable { | ||
var id: Self { self } | ||
|
||
case bubblegum | ||
case buttercup | ||
case indigo | ||
case lavender | ||
case magenta | ||
case navy | ||
case orange | ||
case oxblood | ||
case periwinkle | ||
case poppy | ||
case purple | ||
case seafoam | ||
case sky | ||
case tan | ||
case teal | ||
case yellow | ||
|
||
var accentColor: Color { | ||
switch self { | ||
case .bubblegum, .buttercup, .lavender, .orange, .periwinkle, .poppy, .seafoam, .sky, .tan, | ||
.teal, .yellow: | ||
return .black | ||
case .indigo, .magenta, .navy, .oxblood, .purple: | ||
return .white | ||
} | ||
} | ||
|
||
var mainColor: Color { Color(rawValue) } | ||
|
||
var name: String { rawValue.capitalized } | ||
} |
59 changes: 59 additions & 0 deletions
59
...tation.docc/Tutorials/BuildingSyncUps/02-ListsOfSyncUps/ListsOfSyncUps-01-code-0004.swift
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import Foundation | ||
import IdentifiedCollections | ||
import SwiftUI | ||
import Tagged | ||
|
||
struct SyncUp: Equatable, Identifiable, Codable { | ||
let id: Tagged<Self, UUID> | ||
var attendees: IdentifiedArrayOf<Attendee> = [] | ||
var duration: Duration = .seconds(60 * 5) | ||
var meetings: IdentifiedArrayOf<Meeting> = [] | ||
var theme: Theme = .bubblegum | ||
var title = "" | ||
} | ||
|
||
struct Attendee: Equatable, Identifiable, Codable { | ||
let id: Tagged<Self, UUID> | ||
var name = "" | ||
} | ||
|
||
struct Meeting: Equatable, Identifiable, Codable { | ||
let id: Tagged<Self, UUID> | ||
let date: Date | ||
var transcript: String | ||
} | ||
|
||
enum Theme: String, CaseIterable, Equatable, Identifiable, Codable { | ||
var id: Self { self } | ||
|
||
case bubblegum | ||
case buttercup | ||
case indigo | ||
case lavender | ||
case magenta | ||
case navy | ||
case orange | ||
case oxblood | ||
case periwinkle | ||
case poppy | ||
case purple | ||
case seafoam | ||
case sky | ||
case tan | ||
case teal | ||
case yellow | ||
|
||
var accentColor: Color { | ||
switch self { | ||
case .bubblegum, .buttercup, .lavender, .orange, .periwinkle, .poppy, .seafoam, .sky, .tan, | ||
.teal, .yellow: | ||
return .black | ||
case .indigo, .magenta, .navy, .oxblood, .purple: | ||
return .white | ||
} | ||
} | ||
|
||
var mainColor: Color { Color(rawValue) } | ||
|
||
var name: String { rawValue.capitalized } | ||
} |
2 changes: 2 additions & 0 deletions
2
...tation.docc/Tutorials/BuildingSyncUps/02-ListsOfSyncUps/ListsOfSyncUps-02-code-0001.swift
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
import ComposableArchitecture | ||
import SwiftUI |
7 changes: 7 additions & 0 deletions
7
...tation.docc/Tutorials/BuildingSyncUps/02-ListsOfSyncUps/ListsOfSyncUps-02-code-0002.swift
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import ComposableArchitecture | ||
import SwiftUI | ||
|
||
@Reducer | ||
struct SyncUpsList { | ||
|
||
} |
10 changes: 10 additions & 0 deletions
10
...tation.docc/Tutorials/BuildingSyncUps/02-ListsOfSyncUps/ListsOfSyncUps-02-code-0003.swift
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import ComposableArchitecture | ||
import SwiftUI | ||
|
||
@Reducer | ||
struct SyncUpsList { | ||
@ObservableState | ||
struct State: Equatable { | ||
var syncUps: IdentifiedArrayOf<SyncUp> = [] | ||
} | ||
} |
Oops, something went wrong.