I'm making a Swift MacOS app which interacts with an external device via serial port. I can control the device through the app, but I want to be able to control it even within other apps using AppleScript (all I need is one simple method like tell application "App" to send "string"
). I've searched numerous sources and couldn't find anything helpful.
I have zero knowledge in Obj-C.
Update:
I've read through some other tutorials and kinda got the idea. Unfortunately, I still don't understand how to make one simple method like tell application "App" to send "string"
.
E.g. Spotify Mac app has this string in its .sdef file:
<command name="play track" code="spfyPCtx" description="Start playback of a track.">
<access-group identifier="com.spotify.playback"/>
<cocoa class="SPPlayTrackScriptCommand"/>
<direct-parameter description="the URI of the track to play" type="text"/>
</command>
Their app receives commands like tell application "Spotify" to play track <track>
and executes them. This is exactly what I want.
Unfortunately, I have no idea how they handle this command in the main code. Maybe SPPlayTrackScriptCommand
is implemented somehow.
I've read through many tutorials and still can't find anything about this particular thing.
Update #2
That's what I've managed to achieve:
Scriptable.sdef:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dictionary SYSTEM "file://localhost/System/Library/DTDs/sdef.dtd">
<dictionary title="App Scripting Terminology">
<suite name="Scriptable App Suite" code="ScNo" description="Scriptable App suite.">
<command name="send" code="SeNdtext">
<access-group identifier="*"/>
<direct-parameter description="Command to send" type="text" requires-access="r">
<cocoa key="commandFlag"/>
</direct-parameter>
<result type="boolean" description="Did it do?"/>
</command>
<class name="application" code="capp" description="The application's basic scripting object.">
<cocoa class="ScriptableApplication"/>
<responds-to command="send">
<cocoa method="send:"/>
</responds-to>
</class>
</suite>
</dictionary>
ScriptableApp.swift:
@objc(ScriptableApplication) class ScriptableApplication: NSObject {
func send(_ command: NSScriptCommand) -> Bool {
//let commandFlag = command.evaluatedArguments?["commandFlag"] as? String
print("A")
return true
}
}
Test AppleScript:
tell application "App"
send "string"
end tell
Nothing works and all I get is missing value.
Thank you
So, I managed to do exactly what I wanted after 4 hours of tedious research.
Here's all the code:
Scriptable.sdef:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dictionary SYSTEM "file://localhost/System/Library/DTDs/sdef.dtd">
<dictionary title="App Scripting Terminology">
<suite name="Scriptable App Suite" code="NoSu" description="Scriptable App suite.">
<command name="send" code="NoOnSend">
<cocoa class="App.ScriptableApplicationCommand"/>
<access-group identifier="*"/>
<parameter code="NoCm" name="command" description="Command to send" type="text">
<cocoa key="commandFlag"/>
</parameter>
<result type="text" description="ACK"/>
</command>
</suite>
</dictionary>
ScriptableCommand.swift:
import Foundation
import Cocoa
class ScriptableApplicationCommand: NSScriptCommand {
override func performDefaultImplementation() -> Any? {
let text = self.evaluatedArguments!["commandFlag"] as! String
print(text)
return text
}
}
Test AppleScript:
tell application "Noonecares"
send command "s"
end tell
Turns out App.ScriptableApplicationCommand
is essential. ScriptableApplicationCommand
alone doesn't do a thing.
At least today I learned something new.