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

Fix compiler error in tutorial. #2331

Merged
merged 2 commits into from
Jul 28, 2023
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 19 additions & 10 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/combine-schedulers",
"state" : {
"revision" : "0625932976b3ae23949f6b816d13bd97f3b40b7c",
"version" : "0.10.0"
"revision" : "ec62f32d21584214a4b27c8cee2b2ad70ab2c38a",
"version" : "0.11.0"
}
},
{
Expand Down Expand Up @@ -41,8 +41,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-clocks",
"state" : {
"revision" : "f9acfa1a45f4483fe0f2c434a74e6f68f865d12d",
"version" : "0.3.0"
"revision" : "0fbaebfc013715dab44d715a4d350ba37f297e4d",
"version" : "0.4.0"
}
},
{
Expand All @@ -54,22 +54,31 @@
"version" : "1.0.4"
}
},
{
"identity" : "swift-concurrency-extras",
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-concurrency-extras",
"state" : {
"revision" : "479750bd98fac2e813fffcf2af0728b5b0085795",
"version" : "0.1.1"
}
},
{
"identity" : "swift-custom-dump",
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-custom-dump",
"state" : {
"revision" : "505aa98716275fbd045d8f934fee3337c82ffbd3",
"version" : "0.10.3"
"revision" : "4a87bb75be70c983a9548597e8783236feb3401e",
"version" : "0.11.1"
}
},
{
"identity" : "swift-dependencies",
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-dependencies",
"state" : {
"revision" : "de1a984a71e51f6e488e98ce3652035563eb8acb",
"version" : "0.5.1"
"revision" : "16fd42ae04c6e7f74a6a86395d04722c641cccee",
"version" : "0.6.0"
}
},
{
Expand Down Expand Up @@ -104,8 +113,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swiftui-navigation",
"state" : {
"revision" : "db81007362f998654239021ca9308a264e59d3e2",
"version" : "0.7.2"
"revision" : "2aa885e719087ee19df251c08a5980ad3e787f12",
"version" : "0.8.0"
}
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,17 @@ struct ContactsFeature: Reducer {
state.contacts.remove(id: id)
return .none

case .destination:
return .none

case let .deleteButtonTapped(id: id):
state.destination = .alert(
AlertState {
TextState("Are you sure?")
} actions: {
ButtonState(role: .destructive, action: .confirmDeletion(id: id)) {
TextState("Delete")
}
state.alert = AlertState {
TextState("Are you sure?")
} actions: {
ButtonState(role: .destructive, action: .confirmDeletion(id: id)) {
TextState("Delete")
}
)
}
return .none
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ struct ContactsFeature: Reducer {
state.contacts.remove(id: id)
return .none

case .destination:
return .none

case let .deleteButtonTapped(id: id):
state.destination = .alert(
AlertState {
Expand All @@ -43,8 +46,9 @@ struct ContactsFeature: Reducer {
return .none
}
}
.ifLet(\.$destination, action: /Action.destination) {
Destination()
.ifLet(\.$addContact, action: /Action.addContact) {
AddContactFeature()
}
.ifLet(\.$alert, action: /Action.alert)
}
}
Original file line number Diff line number Diff line change
@@ -1,48 +1,53 @@
struct ContentView: View {
let store: StoreOf<ContactsFeature>
struct ContactsFeature: Reducer {
struct State: Equatable {
var contacts: IdentifiedArrayOf<Contact> = []
@PresentationState var destination: Destination.State?
}
enum Action: Equatable {
case addButtonTapped
case deleteButtonTapped(id: Contact.ID)
case destination(PresentationAction<Destination.Action>)
enum Alert: Equatable {
case confirmDeletion(id: Contact.ID)
}
}
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .addButtonTapped:
state.destination = .addContact(
AddContactFeature.State(
contact: Contact(id: UUID(), name: "")
)
)
return .none

var body: some View {
NavigationStack {
WithViewStore(self.store, observe: \.contacts) { viewStore in
List {
ForEach(viewStore.state) { contact in
HStack {
Text(contact.name)
Spacer()
Button {
viewStore.send(.deleteButtonTapped(id: contact.id))
} label: {
Image(systemName: "trash")
.foregroundColor(.red)
}
}
}
}
.navigationTitle("Contacts")
.toolbar {
ToolbarItem {
Button {
viewStore.send(.addButtonTapped)
} label: {
Image(systemName: "plus")
case let .destination(.presented(.addContact(.delegate(.saveContact(contact))))):
state.contacts.append(contact)
return .none

case let .destination(.presented(.alert(.confirmDeletion(id: id)))):
state.contacts.remove(id: id)
return .none

case .destination:
return .none

case let .deleteButtonTapped(id: id):
state.destination = .alert(
AlertState {
TextState("Are you sure?")
} actions: {
ButtonState(role: .destructive, action: .confirmDeletion(id: id)) {
TextState("Delete")
}
}
}
)
return .none
}
}
.sheet(
store: self.store.scope(state: \.$destination, action: { .destination($0) }),
state: ,
action:
) { addContactStore in
NavigationStack {
AddContactView(store: addContactStore)
}
.ifLet(\.$destination, action: /Action.destination) {
Destination()
}
.alert(
store: self.store.scope(state: \.$destination, action: { .destination($0) }),
state: ,
action:
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ struct ContentView: View {
}
.sheet(
store: self.store.scope(state: \.$destination, action: { .destination($0) }),
state: /ContactsFeature.Destination.State.addContact,
state: ,
action:
) { addContactStore in
NavigationStack {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ struct ContentView: View {
.sheet(
store: self.store.scope(state: \.$destination, action: { .destination($0) }),
state: /ContactsFeature.Destination.State.addContact,
action: ContactsFeature.Destination.Action.addContact
action:
) { addContactStore in
NavigationStack {
AddContactView(store: addContactStore)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ struct ContentView: View {
}
.alert(
store: self.store.scope(state: \.$destination, action: { .destination($0) }),
state: /ContactsFeature.Destination.State.alert,
action: ContactsFeature.Destination.Action.alert
state: ,
action:
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
struct ContentView: View {
let store: StoreOf<ContactsFeature>

var body: some View {
NavigationStack {
WithViewStore(self.store, observe: \.contacts) { viewStore in
List {
ForEach(viewStore.state) { contact in
HStack {
Text(contact.name)
Spacer()
Button {
viewStore.send(.deleteButtonTapped(id: contact.id))
} label: {
Image(systemName: "trash")
.foregroundColor(.red)
}
}
}
}
.navigationTitle("Contacts")
.toolbar {
ToolbarItem {
Button {
viewStore.send(.addButtonTapped)
} label: {
Image(systemName: "plus")
}
}
}
}
}
.sheet(
store: self.store.scope(state: \.$destination, action: { .destination($0) }),
state: /ContactsFeature.Destination.State.addContact,
action: ContactsFeature.Destination.Action.addContact
) { addContactStore in
NavigationStack {
AddContactView(store: addContactStore)
}
}
.alert(
store: self.store.scope(state: \.$destination, action: { .destination($0) }),
state: /ContactsFeature.Destination.State.alert,
action: ContactsFeature.Destination.Action.alert
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -206,18 +206,25 @@
@Code(name: "ContactsFeatures.swift", file: 02-02-02-code-0011)
}

@Step {
We can handle all other destination actions by simply returning a `.none` effect to
represent there is no other work to perform.

@Code(name: "ContactsFeatures.swift", file: 02-02-02-code-0012)
}

@Step {
Update the state mutation for showing an alert to instead point the `destination` to the
`alert` case.

@Code(name: "ContactsFeatures.swift", file: 02-02-02-code-0012)
@Code(name: "ContactsFeatures.swift", file: 02-02-02-code-0013)
}

@Step {
Replace the two `ifLet`s that were used at the bottom of the reducer with a single one that
runs the `Destination` reducer whenever the `destination` state is non-`nil`.

@Code(name: "ContactsFeatures.swift", file: 02-02-02-code-0013)
@Code(name: "ContactsFeatures.swift", file: 02-02-02-code-0014)
}

That's all it takes to convert two independent, imprecisely modeled optional values into a
Expand All @@ -231,29 +238,29 @@
`Destination.State` enum, and an action transformation for embedding a child action into
a particular case of the `Destination.Action` enum.

@Code(name: "ContactsFeatures.swift", file: 02-02-02-code-0014, previousFile: 02-02-02-code-0014-previous)
@Code(name: "ContactsFeatures.swift", file: 02-02-02-code-0015, previousFile: 02-02-02-code-0015-previous)
}

@Step {
The state transformation is responsible for taking a piece of `Destination.State`, which
is an enum, and extracting a particular case from it. We can do this by using a case path
derived from the `addContact` case.

@Code(name: "ContactsFeatures.swift", file: 02-02-02-code-0015)
@Code(name: "ContactsFeatures.swift", file: 02-02-02-code-0016)
}

@Step {
The action transformation is responsible for taking a child action in the
`AddContactFeature` domain and embedding it into the `Destination.Action` enum. This can
be done by using the case of the enum as a function.

@Code(name: "ContactsFeatures.swift", file: 02-02-02-code-0016)
@Code(name: "ContactsFeatures.swift", file: 02-02-02-code-0017)
}

@Step {
The same can be done for the `alert(store:)` view modifier.

@Code(name: "ContactsFeatures.swift", file: 02-02-02-code-0017)
@Code(name: "ContactsFeatures.swift", file: 02-02-02-code-0018)
}

That completes the refactor of the reducer and view to use a single piece of optional enum
Expand Down