简体   繁体   中英

Make SwiftUI app appear in the macOS Dock

I'm a complete noob at macOS UI programming. Last time I did that was sometime in the 20th century using Visual Mac Standard Basic or REALbasic…

I'm trying to build a graphical UI to interact with some algorithms written in Swift using SwiftUI. I don't intend on distributing a runnable application bundle, instead the GUI should be launched from the command line using swift run or similar.

I've managed to get a window with my UI shown on the screen but it doesn't really behave. It doesn't get focus when running the executable from the command line and clicking the window makes it look like the frontmost window but the window from Terminal.app still has the focus and receives keystrokes. There's no app icon in the Dock (even a generic one) and I can't switch to the application using Command-Tab.

What am I missing to get the usual behaviour from the application in this regard?

I'm using macOS 10.15.7 and Xcode 12.4. I want the application to be build using the Swift Package Manager from the command line. Below are the files from the minimal project I'm woking with:

Package.swift
// swift-tools-version:5.3
import PackageDescription

let linkerFlags = [
    "-Xlinker", "-sectcreate",
    "-Xlinker", "__TEXT",
    "-Xlinker", "__info_plist",
    "-Xlinker", "Resources/Info.plist"]

let package = Package(
    name: "ConnectedRegions",
    platforms: [.macOS(.v10_15)],
    products: [
        .executable(name: "ConnectedRegions", targets: ["ConnectedRegions"])],
    targets: [
        .target(
            name: "ConnectedRegions",
            linkerSettings: [.unsafeFlags(linkerFlags)])])
Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>NSPrincipalClass</key>
    <string>NSApplication</string>
</dict>
</plist>
main.swift
import Cocoa
import SwiftUI

class AppDelegate: NSObject, NSApplicationDelegate {
    func applicationDidFinishLaunching(_ notification: Notification) {
        let window = NSWindow(
            contentRect: NSRect(x: 0, y: 0, width: 200, height: 200),
            styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
            backing: .buffered,
            defer: false)
        
        window.center()
        window.contentView = NSHostingView(rootView: Text("Hello").padding(50))
        window.makeKeyAndOrderFront(nil)
    }
}

let delegate = AppDelegate()
NSApplication.shared.delegate = delegate

_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)

With the new App protocol from SwiftUI in macOS 11, which provides an entry point, this is quite easy to achieve. No Info.plist is necessary to get a default menu and Dock icon:

import SwiftUI

@main
struct MyApp: App {
    @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    var body: some Scene {
        WindowGroup {
            VStack {
                Button("Done") { exit(0) }
            }
            .padding(100)
        }
    }
}

class AppDelegate: NSObject, NSApplicationDelegate {
    func applicationDidFinishLaunching(_ notification: Notification) {
        NSApplication.shared.setActivationPolicy(.regular)
        NSApplication.shared.activate(ignoringOtherApps: true)
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM