colima status bar app
Decided to give a try to colima
But somehow still missing some status bar icon displaying if it is running or not
Which immediatelly become my weekend fun
The goal is not the app itself but probably attempt to figure out how to build such apps in general
And indeed that is quite easy, except few caweats, for you to run shell commands you need:
App Sandbox
Under target signing and capabilities - remove app sandbox block
User Script Sandboxing
Under project build settings turn off user script sandboxing
Shell
While running shell script do not forget to pass -l
flag, so user profile will be loaded, otherwise you will catch neverending errors like /bin/zsh colima not found
Dock icon
To disable dock icon add application is agent property to target info and set it to NO
Sample app is so small and easy that probably there is no need for any description
import SwiftUI
@main
struct ColimaMenuApp: App {
@State private var changing: Bool = false
@State private var running: Bool = colima("status").contains("colima is running")
var body: some Scene {
MenuBarExtra("Colima", systemImage: "shippingbox") {
AppMenu(changing: $changing, running: $running)
}
}
}
struct AppMenu: View {
@Binding var changing: Bool
@Binding var running: Bool
var label: String {
if changing {
return "Applying..."
}
return running ? "Stop" : "Start"
}
var body: some View {
Button(action: {
changing = true
DispatchQueue.global(qos: .background).async {
print(colima(self.running ? "stop" : "start"))
DispatchQueue.main.async {
changing = false
running.toggle()
}
}
}, label: { Text(label) }).keyboardShortcut("s").disabled(changing)
Divider()
Button(action: { NSApplication.shared.terminate(nil) }, label: { Text("Quit") }).keyboardShortcut("q")
}
}
func colima(_ command: String) -> String {
return runShellCommand(command: "colima \(command)")
}
func text(_ pipe: Pipe) -> String {
return String(decoding: pipe.fileHandleForReading.readDataToEndOfFile(), as: UTF8.self).trimmingCharacters(in: .whitespacesAndNewlines)
}
func runShellCommand(command: String) -> String {
let process = Process()
let stdout = Pipe()
let stderr = Pipe()
process.executableURL = URL(fileURLWithPath: ProcessInfo.processInfo.environment["SHELL"]!)
process.arguments = ["-l", "-c", command]
process.standardOutput = stdout
process.standardError = stderr
process.environment = ProcessInfo.processInfo.environment
do {
try process.run()
let err = text(stderr)
if err != "" {
return err
}
return text(stdout)
} catch {
let err = text(stderr)
if err != "" {
return err
}
return error.localizedDescription
}
}
With this in place, technically quadrillion of other useful things may be done, for repeated commands
And unfortunately there is no way to share it via app store, because of app sandboxing, so only testflight internal testing
Links that helped: