Most examples of showing Alert
refer to some kind of @State
being used as a binding that controls the presented/hidden state of the alert view.
As an example showingAlert
(source):
struct ContentView : View {
@State var showingAlert = false
var body: some View {
Button(action: {
self.showingAlert = true
}) {
Text("Show Alert")
}
.alert(isPresented: $showingAlert) {
Alert(
title: Text("Important message"),
message: Text("Wear sunscreen"),
dismissButton: .default(Text("Got it!"))
)
}
}
}
It's a good solution when the alert is triggered from the UI layer - as in the example:
Button(action: {
self.showingAlert = true
}
But what if we want to trigger it from the controller/viewmodel layer with a specific message? As an example, we make a network call - the URLSession
’s Publisher
can send Data
or an Error
that we want to push to the user as a message in the Alert
.
@State
is designed to be managed from the view's body
, so it seems that we should rather use an @ObjectBinding
in this case. It seems that we also need some message
, so we can reference it in the body
:
Alert(
title: Text("Important message"),
message: Text(objectBinding.message)
)
The showingAlert
would be a bit redundant here as we may define message
as String?
and create a binding for presentation
:
Binding<Bool>(
getValue: { objectBinding.message != nil },
setValue: { if !$0 { objectBinding.message = nil } }
)
It's a doable approach and it works, but two things are making me a bit anxious:
message
is managed by two abstractionsCan it be done better?
If you specifically want to use Combine and it's publisher mechanism, you might be able to use onReceive()
. Every generic SwiftUI View has onReceive()
on it as a generic function that accepts a publisher, and when instantiated will subscribe to the publisher. It acts very similarly to the single-closure version of the Combine subscriber sink
if you're familiar with that.
The specifics of the publisher expect a publisher Failure type of Never for this to work, so if you're working from Error's within some pipeline, you'll need to convert them to some other kind of object (perhaps an Enum
with associated String
values) and externalize them so they can be displayed with SwiftUI.
How you end up exposing the publisher to the SwiftUI view might be awkward as well - I've been doing it by adding a publisher onto a reference object with other @Published
properties.
You can see a rough example of how this works (although not your specific use case) in Using Combine at https://heckj.github.io/swiftui-notes/#pattern-observableobject
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With