Skip to content

Commit 2500304

Browse files
authored
Alert/ConfirmationDialog updates (pointfreeco#68)
* Always open up closures with global actors Going point-free style when `async` is involved can be problematic, and lose things like the current actor. * Support async confirmation dialog actions
1 parent 1db1bcf commit 2500304

File tree

4 files changed

+40
-32
lines changed

4 files changed

+40
-32
lines changed

Examples/Inventory/ItemRow.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,10 @@ struct ItemRowView: View {
113113
.foregroundColor(self.model.item.status.isInStock ? nil : Color.gray)
114114
.alert(
115115
unwrapping: self.$model.destination,
116-
case: /ItemRowModel.Destination.alert,
117-
action: self.model.alertButtonTapped
118-
)
116+
case: /ItemRowModel.Destination.alert
117+
) {
118+
self.model.alertButtonTapped($0)
119+
}
119120
.popover(
120121
unwrapping: self.$model.destination,
121122
case: /ItemRowModel.Destination.duplicate

Sources/SwiftUINavigation/Alert.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ extension View {
113113
/// populate the fields of an alert that the system displays to the user. When the user
114114
/// presses or taps one of the alert's actions, the system sets this value to `nil` and
115115
/// dismisses the alert, and the action is fed to the `action` closure.
116-
/// - action: A closure that is called with an action from a particular alert button when
116+
/// - handler: A closure that is called with an action from a particular alert button when
117117
/// tapped.
118118
@available(iOS 15, macOS 12, tvOS 15, watchOS 8, *)
119119
public func alert<Value>(
@@ -152,15 +152,15 @@ extension View {
152152
/// When the user presses or taps one of the alert's actions, the system sets this value to
153153
/// `nil` and dismisses the alert, and the action is fed to the `action` closure.
154154
/// - casePath: A case path that identifies a particular case that holds alert state.
155-
/// - action: A closure that is called with an action from a particular alert button when
155+
/// - handler: A closure that is called with an action from a particular alert button when
156156
/// tapped.
157157
@available(iOS 15, macOS 12, tvOS 15, watchOS 8, *)
158158
public func alert<Enum, Value>(
159159
unwrapping `enum`: Binding<Enum?>,
160160
case casePath: CasePath<Enum, AlertState<Value>>,
161-
action: @escaping (Value) async -> Void = { (_: Void) async in }
161+
action handler: @escaping (Value) async -> Void = { (_: Void) async in }
162162
) -> some View {
163-
self.alert(unwrapping: `enum`.case(casePath), action: action)
163+
self.alert(unwrapping: `enum`.case(casePath), action: handler)
164164
}
165165
#else
166166
@available(iOS 15, macOS 12, tvOS 15, watchOS 8, *)
@@ -206,9 +206,9 @@ extension View {
206206
public func alert<Enum, Value>(
207207
unwrapping `enum`: Binding<Enum?>,
208208
case casePath: CasePath<Enum, AlertState<Value>>,
209-
action: @escaping (Value) async -> Void
209+
action handler: @escaping (Value) async -> Void
210210
) -> some View {
211-
self.alert(unwrapping: `enum`.case(casePath), action: action)
211+
self.alert(unwrapping: `enum`.case(casePath), action: handler)
212212
}
213213

214214
@available(iOS 15, macOS 12, tvOS 15, watchOS 8, *)

Sources/SwiftUINavigation/ConfirmationDialog.swift

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,12 @@ extension View {
121121
/// to populate the fields of a dialog that the system displays to the user. When the user
122122
/// presses or taps one of the dialog's actions, the system sets this value to `nil` and
123123
/// dismisses the dialog, and the action is fed to the `action` closure.
124-
/// - action: A closure that is called with an action from a particular dialog button when
124+
/// - handler: A closure that is called with an action from a particular dialog button when
125125
/// tapped.
126126
@available(iOS 15, macOS 12, tvOS 15, watchOS 8, *)
127127
public func confirmationDialog<Value>(
128128
unwrapping value: Binding<ConfirmationDialogState<Value>?>,
129-
action: @escaping (Value) -> Void = { (_: Never) in fatalError() }
129+
action handler: @escaping (Value) async -> Void = { (_: Void) async in }
130130
) -> some View {
131131
self.confirmationDialog(
132132
value.wrappedValue.flatMap { Text($0.title) } ?? Text(""),
@@ -135,7 +135,11 @@ extension View {
135135
presenting: value.wrappedValue,
136136
actions: {
137137
ForEach($0.buttons) {
138-
Button($0, action: action)
138+
Button($0) { action in
139+
Task {
140+
await handler(action)
141+
}
142+
}
139143
}
140144
},
141145
message: { $0.message.map { Text($0) } }
@@ -155,24 +159,24 @@ extension View {
155159
/// When the user presses or taps one of the dialog's actions, the system sets this value to
156160
/// `nil` and dismisses the dialog, and the action is fed to the `action` closure.
157161
/// - casePath: A case path that identifies a particular case that holds dialog state.
158-
/// - action: A closure that is called with an action from a particular dialog button when
162+
/// - handler: A closure that is called with an action from a particular dialog button when
159163
/// tapped.
160164
@available(iOS 15, macOS 12, tvOS 15, watchOS 8, *)
161165
public func confirmationDialog<Enum, Value>(
162166
unwrapping `enum`: Binding<Enum?>,
163167
case casePath: CasePath<Enum, ConfirmationDialogState<Value>>,
164-
action: @escaping (Value) -> Void = { (_: Never) in fatalError() }
168+
action handler: @escaping (Value) async -> Void = { (_: Void) async in }
165169
) -> some View {
166170
self.confirmationDialog(
167171
unwrapping: `enum`.case(casePath),
168-
action: action
172+
action: handler
169173
)
170174
}
171175
#else
172176
@available(iOS 15, macOS 12, tvOS 15, watchOS 8, *)
173177
public func confirmationDialog<Value>(
174178
unwrapping value: Binding<ConfirmationDialogState<Value>?>,
175-
action: @escaping (Value) -> Void
179+
action handler: @escaping (Value) async -> Void
176180
) -> some View {
177181
self.confirmationDialog(
178182
value.wrappedValue.flatMap { Text($0.title) } ?? Text(""),
@@ -181,12 +185,17 @@ extension View {
181185
presenting: value.wrappedValue,
182186
actions: {
183187
ForEach($0.buttons) {
184-
Button($0, action: action)
188+
Button($0) { action in
189+
Task {
190+
await handler(action)
191+
}
192+
}
185193
}
186194
},
187195
message: { $0.message.map { Text($0) } }
188196
)
189197
}
198+
190199
@available(iOS 15, macOS 12, tvOS 15, watchOS 8, *)
191200
public func confirmationDialog(
192201
unwrapping value: Binding<ConfirmationDialogState<Never>?>
@@ -196,17 +205,19 @@ extension View {
196205
action: { (_: Never) in fatalError() }
197206
)
198207
}
208+
199209
@available(iOS 15, macOS 12, tvOS 15, watchOS 8, *)
200210
public func confirmationDialog<Enum, Value>(
201211
unwrapping `enum`: Binding<Enum?>,
202212
case casePath: CasePath<Enum, ConfirmationDialogState<Value>>,
203-
action: @escaping (Value) -> Void
213+
action handler: @escaping (Value) async -> Void
204214
) -> some View {
205215
self.confirmationDialog(
206216
unwrapping: `enum`.case(casePath),
207-
action: action
217+
action: handler
208218
)
209219
}
220+
210221
@available(iOS 15, macOS 12, tvOS 15, watchOS 8, *)
211222
public func confirmationDialog<Enum>(
212223
unwrapping `enum`: Binding<Enum?>,

Sources/SwiftUINavigation/Documentation.docc/Articles/AlertsDialogs.md

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,9 @@ struct ContentView: View {
7878
List {
7979
// ...
8080
}
81-
.alert(
82-
unwrapping: self.$model.alert,
83-
action: self.alertButtonTapped
84-
)
81+
.alert(unwrapping: self.$model.alert) { action in
82+
self.model.alertButtonTapped(action)
83+
}
8584
}
8685
}
8786
```
@@ -129,11 +128,9 @@ With this kind of set up you can use an alternative `alert` view modifier that t
129128
argument for specifying which case of the enum drives the presentation of the alert:
130129

131130
```swift
132-
.alert(
133-
unwrapping: self.$model.destination,
134-
case: /Destination.alert,
135-
action: self.alertButtonTapped
136-
)
131+
.alert(unwrapping: self.$model.destination, case: /Destination.alert) { action in
132+
self.model.alertButtonTapped(action)
133+
}
137134
```
138135

139136
Note that the `case` argument is specified via a concept known as "case paths", which are like
@@ -185,10 +182,9 @@ struct ContentView: View {
185182
List {
186183
// ...
187184
}
188-
.confirmationDialog(
189-
unwrapping: self.$model.dialog,
190-
action: self.dialogButtonTapped
191-
)
185+
.confirmationDialog(unwrapping: self.$model.dialog) { action in
186+
self.dialogButtonTapped(action)
187+
}
192188
}
193189
}
194190
```

0 commit comments

Comments
 (0)