I am developing a simple menu bar app on mac. It has a window containing a button saying "do something", of course, to simulate a network request. During said request, an error may happen, in which case, an alert should show containing details. This error comes from a ViewModel and whenever it is set from nil to a string value, the alert should appear.
The "OK" button in the alert should dismiss it by setting the view model's error message back to nil.
Here is my implementation of the ViewModel.swift file:
import Foundation
@Observable
class ViewModel {
var errorMessage: String?
func doSomething() {
self.errorMessage = "The image could not be downloaded"
}
}
And here is my ContentView.swift:
import SwiftUI
struct ContentView: View {
@State var viewModel = ViewModel()
var body: some View {
VStack {
Button("Do something") {
viewModel.doSomething()
}
}
.alert(viewModel.errorMessage ?? "", isPresented: Binding(get: {
viewModel.errorMessage != nil
}, set: { value in
viewModel.errorMessage = nil
})) {
Button("OK") {
viewModel.errorMessage = nil
// Here the view should be refreshed, then the "viewModel.errorMessage != nil" would hide the alert
}
}
}
}
And the app entry point:
@main
struct Macos_SwiftUI_Alert_issueApp: App {
var body: some Scene {
MenuBarExtra("", systemImage: "square.fill") {
ContentView()
.frame(width: 500, height: 500)
}
.menuBarExtraStyle(.window)
}
}
I press the "Do something" button in the view (which causes the alert to appear), but when I press the "OK" button in the alert, the whole app view disappears, instead of just the alert disappearing. Also, when I open the menu bar app again, the alert is still present. After I press "OK" for a second time the whole view disappears again, and the third time I open my app, the alert finally goes. Has anyone seen this before or has a fix? Thanks in advance. Maybe I shouldn't even be using an alert to display an error message...?
I found a workaround to this problem a while later, that is, instead of using the SwiftUI .alert
modifier, you can use the older AppKit NSAlert
and make a function called showError
to:
NSAlert
import SwiftUI
struct ContentView: View {
@Environment(\.dismiss) private var dismiss
var body: some View {
VStack {
Button("Show error") {
showError()
}
}
}
func showError() {
let alert = NSAlert()
alert.messageText = "An error occurred"
alert.alertStyle = .warning
dismiss()
alert.runModal()
}
}