Reputation: 247
I have searched all document about how to present alert in swiftui, but they all show code like this:
Button(action: {
self.showingAlert = true
}) {
Text("Show Alert")
}
.alert(isPresented:$showingAlert) {
Alert(title: Text("title"))
}
Which means that alert must be used for button.
How can I use alert in my custom class like handle http request and then show error alert like this:
class A{
func getDate(){
alert("error")
}
}
Upvotes: 3
Views: 4713
Reputation: 501
We need to Create a UserState class and conform to the ObservableObject protocol. Classes that conform to the ObservableObject protocol can use SwiftUI’s @Published property wrapper to automatically announce changes to properties, so that any views using the object get their body property reinvoked and stay in sync with their data.
//MARK:- Step:1 On SceneDelegate
// set environmentObject
let userState = UserState()
let contentView = ContentView().environmentObject(userState)
//MARK:- Step:2 Create UserState Class
class UserState: ObservableObject {
@Published var sessionExpired: Bool = false
}
//MARK:- Step:3 Create a ContentView
struct ContentView: View {
// Step:3 Create a userState EnvironmentObject
@EnvironmentObject var userState: UserState
@State private var isShowSecondView = false
@State private var showingAlert = false
var body: some View {
NavigationView {
NavigationLink(destination: SecondView(), isActive: $isShowSecondView) {
ZStack(alignment: .center) {
Button("Show Second") {
isShowSecondView.toggle()
}
}
.alert(isPresented: $showingAlert) {
Alert(title: Text("Alert"), message: Text("Session Expired"), dismissButton: .default(Text("Got it!")))
}
.onChange(of: userState.sessionExpired, perform: { value in // Step:4 in onChange we need to check sessionExpired or not
print("onChange")
if userState.sessionExpired {
showingAlert = true
}
})
}
}
}
}
//MARK:-
struct SecondView: View {
@EnvironmentObject var userState: UserState
var body: some View {
ZStack(alignment: .center) {
Button("Show Alert Message") { // Step:5 - Here I changed flag then alert will present
userState.sessionExpired.toggle()
}
}
}
}
Upvotes: 2
Reputation: 2792
Another way to solve it, if you want to use custom error description, then create your custom error which conforms to the Identifiable
protocol like below:
enum NetworkError: LocalizedError, Identifiable {
case notFound
case serverError(responseCode: Int)
case underlyingError(Error)
case unknown
var id: String { localizedDescription }
var errorDescription: String? {
switch self {
case .notFound: return "Not found"
case .serverError(let responseCode): return "Server error \(responseCode)"
case .underlyingError(let error): return error.localizedDescription
case .unknown: return "Unknown error"
}
}
}
Inside your viewModel, then set the error you'll get (from your api etc.) and set it to your published error.
class YourViewModel: ObservableObject {
@Published var persons: [Person]
@Published var error: NetworkError? // <- Set your error from your api here
private var subscriptions = Set<AnyCancellable>()
init() {
fetch()
}
func fetch() {
api.people
.sink(receiveCompletion: { [weak self] (completion) in
if case .failure(let error) = completion {
self?.error = error // An error occured
}
}, receiveValue: { [weak self] (persons) in
self?.onReceive(persons)
self?.error = nil
})
.store(in: &subscriptions)
}
//...//
}
The published error will trigger the alert and you'll be able to present the errors' localizedDescription
within the closure.
struct MainView: View {
@ObservedObject var viewModel = YourViewModel()
var body: some View {
Text(viewModel.persons.first?.name ?? "N/A")
.alert(item: self.$viewModel.error) { error in
Alert(title: Text("Error"),
message: Text(error.localizedDescription),
dismissButton: .cancel())
}
}
}
Upvotes: 2
Reputation: 529
Create a class and conform to the ObservableObject
protocol (Anything that conforms to ObservableObject can be used inside SwiftUI, and publish announcements when its values have changed so the user interface can be updated):
class A: ObservableObject {
@Published var showAlert = false
func buttonTapped() {
//handle request and then set to true to show the alert
self.showAlert = true
}
}
Your View:
struct ContentView: View {
@ObservedObject private var viewModel = A()
var body: some View {
Button(action: {
self.viewModel.buttonTapped()
}) {
Text("Show Alert")
}
.alert(isPresented: $viewModel.showAlert, content: { () -> Alert in
Alert(title: Text("Error"), message: Text("Please try again"), dismissButton: .default(Text("Okay")))
})
}
}
Upvotes: 3