Hi, I'm trying to create a reusable custom alert with its own reducer. This will not use the .alert
view modifier but a ViewModifier
instead, in order to customize all the UI.
Right now I'm working on the Reducer and found a brick wall that makes me think again about my approach. This is my Reducer code.
struct CustomAlertReducer<ButtonAction: Equatable>: Reducer {
@Dependency(\.dismiss) var dismiss
struct State: Equatable {
public var title: String?
public var message: String?
public var image: URL?
public var buttons: [CustomAlertButtonState<ButtonAction>]
public var bottomButton: CustomAlertButtonState<ButtonAction>?
}
enum Action: Equatable {
case cancel
case action(ButtonAction)
}
var body: some Reducer<State, Action> {
Reduce { state, action in
switch action {
case .cancel:
return .run { _ in
await self.dismiss()
}
case .action:
// The actions should be handled by the reducer displaying the alert
return .none
}
}
}
}
struct CustomAlertButtonState<Action>: Identifiable, Equatable {
public let id: UUID
public let text: String
public let action: Action
init(
text: String,
action: Action
) {
self.id = UUID()
self.text = text
self.action = action
}
public static func == (lhs: CustomAlertButtonState, rhs: CustomAlertButtonState) -> Bool {
lhs.text == rhs.text
}
}
Going this way is obviously forcing me to add the in the view for CustomAlertReducer
in order to be able to use it, forcing me to this weird code
struct CustomAlert<ButtonAction: Equatable>: View {
let viewStore: ViewStore<CustomAlertReducer<ButtonAction>.State?, CustomAlertReducer<ButtonAction>.Action>
...
}
The use of an optional state on the ViewStore is due to the ability to deal with state being nil
and then hiding the view modifier.
Should be this the proper way to handle this situation? Not sure if this is the best approach to deal with ViewModifiers on TCA (with present and dismiss animations), but is the best I could find until now.
My final goal is to deal with the ViewModifiers visibility using an Alert
reducer similar to the Destination
one described on the documentation
struct MyReducer: Reducer {
struct State: Equatable {
@PresentationState var alert: Alert.State?
//...
}
struct Alert: Reducer {
enum State: Equatable {
case whisper(WhisperReducer.State) // This is another kind of custom alert
case customAlert(CustomAlertReducer.State)
}
enum Action: Equatable {
case whisper(WhisperReducer.Action)
case customAlert(CustomAlertReducer.Action)
}
var body: some ReducerOf<Self> {
Scope(state: /State.whisper, action: /Action.whisper) {
WhisperReducer()
}
Scope(state: /State.customAlert, action: /Action.customAlert) {
CustomAlertReducer()
}
}
}
var body: some Reducer<State, Action> {
Reduce { state, action in
//...
}
.ifLet(\.$alert, action: /Action.alert) {
Alert()
}
}
}
Thanks for your time.