Search code examples
swiftmacosswiftuialertmenubarextra

SwiftUI Alert not displaying properly in a MacOS menu Bar App


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)
    }
}

My issue:

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...?

enter image description here


Solution

  • 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:

    1. Make an NSAlert
    2. Set the text in the message
    3. (optional) Set the alert style
    4. (optional) dismiss the menu window
    5. Show the alert

    Here is the code to fix the problem:

    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()
        }
    
    }