Search code examples
swiftmacospickersirishortcuts

Display Siri Shortcuts programs in a picker - Swift (macOS)


I'm developing a Swift application for macOS. I would like it to be able to interact with the user's shortcuts (block programs in the Shortcuts application, fig. 1). But I can't locate them...

Do you know where they are located and how I could display them in a picker (fig. 2) so that they can be selected by the user in the app?

Thanks in advance!

Fig. 1

Siri Shortcuts

Fig. 2

Picker


Solution

  • You can get the shortcuts' names using the shortcuts list command. You can run this in Swift using a Process. Here is a simple example:

    struct ContentView: View {
        @State var shortcuts = [ShortcutInfo]()
        @State var selection = ""
        var body: some View {
            Picker("Pick a shortcut", selection: $selection) {
                ForEach(shortcuts) {
                    Text($0.name)
                }
            }
            .task {
                let shortcuts = (try? await loadShortcutNames()) ?? []
                self.shortcuts = shortcuts
                selection = shortcuts.first?.id ?? ""
            }
    
            // use "shortcuts run" to run the shortcut
            // there additional options to specify input
            // see "shortcuts help run" for more info
            Button("Run") {
                let process = Process()
                process.executableURL = URL(filePath: "/usr/bin/shortcuts")
                process.arguments = ["run", selection]
                try? process.run()
            }
        }
    }
    
    struct ShortcutInfo: Identifiable {
        let id: String
        let name: String
    }
    
    func loadShortcutNames() async throws -> [ShortcutInfo] {
        let process = Process()
        process.executableURL = URL(filePath: "/usr/bin/shortcuts")
    
        // also get the UUID of the shortcut so that ShortcutInfo can be Identifiable
        process.arguments = ["list", "--show-identifiers"]
        let pipe = Pipe()
        process.standardOutput = pipe
        return try await withTaskCancellationHandler {
            try process.run()
            return try await pipe.fileHandleForReading.bytes.lines.compactMap { line in
    
                // parse the shortcut name and ID
                guard let (_, name, id) = line.wholeMatch(of: /(.*) \(([A-Z0-9-]*)\)/)?.output else {
                    return nil
                }
                return ShortcutInfo(id: String(id), name: String(name))
            }.reduce(into: []) { $0.append($1) }
        } onCancel: {
            process.terminate()
        }
    }
    

    For the icon and background color of the shortcuts though, they are stored in ~/Library/Shortcuts/Shortcuts.sqlite. You would need a SQLite client to read the database, and reverse-engineer the data. This would be quite fragile, as the database schema could very well change in future versions of macOS, so I wouldn't recommend that.

    Hopefully there will be a proper API for accessing Shortcuts in the future.