命令行Swift脚本的最低可行GUI是什么?

命令行Swift脚本的最低可行GUI是什么?,swift,macos,cocoa,appkit,Swift,Macos,Cocoa,Appkit,我需要一个小助手图形用户界面为我的Swift脚本在macOS上。它只需要一个文本输入字段和一个OK按钮 我不想为了这个小小的弹出窗口而走整个臃肿的Xcode路线。然而,苹果的文档让我失望,因为我的NSWindow没有捕获键盘输入。救命啊 不,多亏了,我终于想出了魔法咒语我写了一个小脚本,可以用swift显示窗口。目标是显示几个shell命令的输出(brew update&brew upgrade&brew cleanup&brew doctor)。如果您不每天执行这些命令,那么这些命令可能会花费

我需要一个小助手图形用户界面为我的Swift脚本在macOS上。它只需要一个文本输入字段和一个OK按钮


我不想为了这个小小的弹出窗口而走整个臃肿的Xcode路线。然而,苹果的文档让我失望,因为我的NSWindow没有捕获键盘输入。救命啊

不,多亏了,我终于想出了魔法咒语我写了一个小脚本,可以用swift显示窗口。目标是显示几个shell命令的输出(
brew update
&
brew upgrade
&
brew cleanup
&
brew doctor
)。如果您不每天执行这些命令,那么这些命令可能会花费大量的时间,我已经厌倦了只需等待10分钟就可以完成前两个命令

我本可以简单地启动cron作业或使用shell脚本启动,但我希望能够检查命令的成功或失败,尤其是
brew doctor
,以了解是否需要执行某些操作来清理我的机器上的自制安装

所以我需要一个窗口来显示错误和命令的标准输出等等,我想生成一个二进制文件

在Google和Github上搜索了一段时间后,我找到了swift sh,它允许导入Github存储库(通过swift Package Manager以标准化的方式),并在swift脚本中使用它,如果需要的话进行编译;和ShellOut,由同一个人开发,它允许从swift脚本执行shell命令,并在swift对象中收集命令的输出

基本上,它应该是一个小窗口,在scrollview中有一个textview,可以滚动显示shell命令的输出

下面是脚本:


#!/usr/bin/swift sh


import AppKit
import Foundation
// importing ShellOut from GitHub repository
// The magic of swift-sh happens
import ShellOut // @JohnSundell

// Declare the Application context
let app = NSApplication.shared

// Create the delegate class responsible for the window and crontrol creation
class AppDelegate: NSObject, NSApplicationDelegate {
    var str: String? = ""

    // Construct the window
    let theWindow = NSWindow(contentRect: NSMakeRect(200, 200, 400, 200),
                          styleMask: [.titled, .closable, .miniaturizable, .resizable],
                          backing: .buffered,
                          defer: false,
                          screen: nil)

    var output: String? = ""

    // What happens once application context launched
    func applicationDidFinishLaunching(_ notification: Notification) {

        var str = ""

        // The shell commands and the collect of output
        do {
            str =  try shellOut(to: "brew", arguments: ["update"] )
            output = output! + str
        } catch {
            let error1 = error as! ShellOutError
            //print(error1.message)
            output = output! + error1.message
        }

        do {
            str = try shellOut(to: "brew", arguments: ["upgrade"] )
            output = output! + "\n" + str
            //print("step 2")
        } catch {
            let error2 = error as! ShellOutError
            //print(error2.message)
            output = output! + "\n" + error2.message
        }

        do {
            str = try shellOut(to: "brew", arguments: ["cleanup"] )
            output = output! + "\n" + str
            //print("step 3")
        } catch {
            let error3 = error as! ShellOutError
            //print(error3.message)
            output = output! + "\n" + error3.message
        }

        do {
            str = try shellOut(to: "brew", arguments: ["doctor"] )
            output = output! + "\n" + str
             //print("step 4")
        } catch {
            let error4 = error as! ShellOutError
            //print(error4.message)
            output = output! + "\n" + error4.message
        }

        // Controls placement and content goes here
        // ScrollView...
        var theScrollview = NSScrollView(frame: theWindow.contentView!.bounds)
        var contentSize = theScrollview.contentSize
        theScrollview.borderType = .noBorder
        theScrollview.hasVerticalScroller = true
        theScrollview.hasHorizontalScroller = false
        theScrollview.autoresizingMask = NSView.AutoresizingMask(rawValue: NSView.AutoresizingMask.width.rawValue | NSView.AutoresizingMask.height.rawValue | NSView.AutoresizingMask.minYMargin.rawValue | NSView.AutoresizingMask.minYMargin.rawValue)

        // TextView...
        var theTextView = NSTextView(frame: NSMakeRect(0, 0, contentSize.width, contentSize.height))
        theTextView.minSize = NSMakeSize(0.0, contentSize.height)
        theTextView.maxSize = NSMakeSize(CGFloat.greatestFiniteMagnitude, CGFloat.greatestFiniteMagnitude)
        theTextView.isVerticallyResizable = true
        theTextView.isHorizontallyResizable = false
        theTextView.autoresizingMask = NSView.AutoresizingMask(rawValue: NSView.AutoresizingMask.width.rawValue | NSView.AutoresizingMask.height.rawValue | NSView.AutoresizingMask.minYMargin.rawValue | NSView.AutoresizingMask.minYMargin.rawValue)
        theTextView.textContainer?.containerSize = NSMakeSize(contentSize.width, CGFloat.greatestFiniteMagnitude)
        theTextView.backgroundColor = .white
        theTextView.textContainer?.widthTracksTextView = true
        theTextView.textStorage?.append(NSAttributedString(string: output!))

        theScrollview.documentView = theTextView

        theWindow.contentView = theScrollview
        theWindow.makeKeyAndOrderFront(nil)
        theWindow.makeFirstResponder(theTextView)
    }

    // What happens when we click the close button of the window
    func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
      return true;
    }
}

// Instantiation of the application delegate class
// and launching
let delegate = AppDelegate()
    app.delegate = delegate
    app.run()

尝试event monitor@MarekH谢谢,我仔细研究并尝试了它,但后来偶然发现了解决方案:
app.setActivationPolicy(.regular)
如果您添加
#/usr/bin/swift
开始时,您也可以将其作为脚本运行。您需要在app.run之前激活app.activate(忽略其他应用程序:true)以使应用程序窗口显示在所有其他应用程序之上。