Skip to content

Commit

Permalink
Fix compiler error in tutorial. (#2331)
Browse files Browse the repository at this point in the history
* Fix compiler error in tutorial.

* wip
  • Loading branch information
mbrandonw authored Jul 28, 2023
1 parent db2a55f commit cf29ea1
Show file tree
Hide file tree
Showing 10 changed files with 144 additions and 70 deletions.
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

0 comments on commit cf29ea1

Please sign in to comment.