Skip to content

Instantly share code, notes, and snippets.

@GeorgeLyon
Last active July 4, 2025 02:15
Show Gist options
  • Select an option

  • Save GeorgeLyon/bbd443dcabef5ca5ae71ae83db6524ef to your computer and use it in GitHub Desktop.

Select an option

Save GeorgeLyon/bbd443dcabef5ca5ae71ae83db6524ef to your computer and use it in GitHub Desktop.
#!/usr/bin/env xcrun -sdk macosx swift
// Usage: `echo "Bar" | ./swift-ui-commandline-tool.swift`
import Foundation
import Cocoa
import SwiftUI
extension CommandLine {
static let input: String = { AnyIterator { readLine(strippingNewline: false) }.joined() }()
}
struct ContentView: View {
var body: some View {
VStack {
Text("Hello, George!")
Button("Print \"Foo\"") { print("Foo") }
Button("Echo Input") { print(CommandLine.input) }
Button("Done") { exit(0) }
}
.padding(100)
}
}
class AppDelegate: NSObject, NSApplicationDelegate {
var window: NSWindow!
func applicationDidFinishLaunching(_ aNotification: Notification) {
let contentView = ContentView()
let window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
styleMask: [.titled, .closable, .fullSizeContentView],
backing: .buffered, defer: false)
window.center()
window.contentView = NSHostingView(rootView: contentView)
window.titlebarAppearsTransparent = true
window.level = .modalPanel
// The default close action causes a crash, and this way we can set the exit status
let closeButton = window.standardWindowButton(.closeButton)!
closeButton.target = self
closeButton.action = #selector(close)
window.makeKeyAndOrderFront(nil)
}
@objc func close() {
exit(1)
}
}
let application = NSApplication.shared
let delegate = AppDelegate()
application.delegate = delegate
application.run()
@rchrd2
Copy link
Copy Markdown

rchrd2 commented May 20, 2023

This is amazing. Thank you!

@GeorgeLyon
Copy link
Copy Markdown
Author

Happy to help, this code is a bit old so thank you for checking it still works :)

@markiv
Copy link
Copy Markdown

markiv commented Apr 5, 2024

Very cool! For me the window wouldn't open unless:

DispatchQueue.main.async {
    NSApplication.shared.setActivationPolicy(.regular)
    NSApplication.shared.activate(ignoringOtherApps: true)
}
App.main()

@GeorgeLyon
Copy link
Copy Markdown
Author

Thanks! Yeah I haven't re-ran this in some time so it probably is a bit out of date.

@Samasaur1
Copy link
Copy Markdown

You shouldn't need to run NSApplication.shared.setActivationPolicy(.regular) inside an async block. I'd also recommend running NSApplication.shared.activate() inside an onAppear modifier attached to a view, rather than in an async block

@johncwelch
Copy link
Copy Markdown

That actually works really well other than the input not working.

@johncwelch
Copy link
Copy Markdown

never mind, it works, I had the dumb

@GeorgeLyon
Copy link
Copy Markdown
Author

Oh, no not the dumb! Glad folks are still finding this useful!

@johncwelch
Copy link
Copy Markdown

It's very useful. I was wondering if you'd ever played around with setting it up to take multiple parameters that might be of different types, so like:

swift-ui-commandline-tool -myInt -"myString"?

@GeorgeLyon
Copy link
Copy Markdown
Author

Swift argument parser (https://github.com/apple/swift-argument-parser) is pretty good for this but it doesn’t work well with scripts. Some attempts have been made to add functionality to make this work better but as far as I know nothing has landed in the tool chain.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment