SwiftUI: How can you make your app come to the front when clicking on the dock icon of the app?

Viewed 281

When a SwiftUI app is minimized and the dock icon is clicked. The app won't be deminimized and put to the front just like other apps do.

import SwiftUI

@main
struct MyApp: App {
    @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    var body: some Scene {
        WindowGroup {
            MainView()
        }
    }
}

class AppDelegate: NSObject, NSApplicationDelegate {
    func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
        // THIS IS NEVER CALLED!!!
        if !flag {
            for window: AnyObject in sender.windows {
                window.makeKeyAndOrderFront(self)
            }
        }
        
        return true
    }
}

Other delegate methods like applicationDidLaunch do get called so its not a linking issue. Does anyone know how to get this to work?

Comment on Asperi enter image description here

4 Answers

Use did become active callback, tested as worked with Xcode 13.3 / macOS 12.2.1

Like,

func applicationDidBecomeActive(_ notification: Notification) {
    if NSApp.windows.compactMap({ $0.isVisible ? Optional(true) : nil }).isEmpty {
         NSApp.windows.first?.makeKeyAndOrderFront(self)
    }
}

Test module in project is here

Note: in case of active application the only application(Will/Did)Update are called in click Dock icon.

In delegate methods that get called when clicking on the dock icon try calling

NSApp.activate(ignoringOtherApps: true)

This should bring your app to the foreground

This is a temporary work around that works for me

class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
    // disable tabs ...
    NSWindow.allowsAutomaticWindowTabbing = false
}

// Quit if the main window is closed
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
    BzLogger[Self.self].info("")
    return true
}

func applicationDidUpdate(_ notification: Notification) {
    if NSApplication.shared.mainWindow == nil,
       let event = NSApplication.shared.currentEvent {
        if event.type == .systemDefined {
            // FIX-ME: kdeda
            // This is hack since Apple broke this whole thing on macOS
            
            // BzLogger[Self.self].info("NSApplication.shared.currentEvent: \(event)")
            // BzLogger[Self.self].info("NSApplication.shared.currentEvent: \(event.subtype)")
            // BzLogger[Self.self].info("NSApplication.shared.pressedMouseButtons: \(NSEvent.pressedMouseButtons)")
            // BzLogger[Self.self].info("NSApplication.shared.isActive: \(NSApplication.shared.isActive)")
            if NSEvent.pressedMouseButtons == 1 {
                // mouse down, maybe there is a better way
                if NSApp.windows.compactMap({ $0.isVisible ? Optional(true) : nil }).isEmpty {
                    NSApp.windows.first?.makeKeyAndOrderFront(self)
                }
            }
        }
    }
}

}

I found this solution.

In your @main entry class App add this property:

@NSApplicationDelegateAdaptor var appDelegate: AppDelegate

It now should looks like this:

import SwiftUI

@main
struct TestApp: App {
    @NSApplicationDelegateAdaptor var appDelegate: AppDelegate
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

Now, create the AppDelegate class (you can create a new separate AppDelegate.swift file or just continue typing after the App struct closed:

class AppDelegate: NSObject, NSApplicationDelegate {
    func applicationDidChangeOcclusionState(_ notification: Notification) {
        if let window = NSApp.windows.first, window.isMiniaturized {
            NSApp.hide(self)
        }
    }
    
    func applicationDidBecomeActive(_ notification: Notification) {
        NSApp.windows.first?.makeKeyAndOrderFront(self)
    }
}

Now it is minimizing, maximizing, closing and reopening again as normal as other apps (ex. Safari, Mail, etc.).

This works perfectly on Monterey 12.4.

Related