How to run AppleScript in macOS

Issue #223

Surround script by single quotes

1
2
3
4
5
6
7
8
9
10
11
12
let script = 
"""
tell application "XcodeWay"
activate
end tell
"""

let command = "osascript -e '\(script)'"

let process = Process()
process.launchPath = "/bin/bash"
process.arguments = ["-c", command]

Run as administrator

In terminal, we can

1
2
cd ~/Library
sudo mkdir MyNewFolder

In code, we use with administrator privileges, this when run will ask for password or fingerprint

1
do shell script "mkdir MyNewFolder" with administrator privileges

String manipulation in Apple Script

Issue #89

Today I find that AppleScript allows us to import Foundation, with that we have lots of power, including NSString. See my script

1
2
3
use scripting additions
use framework "Foundation"
property NSString : a reference to current application's NSString

Here is how I can remove last path component from a string

1
2
3
4
5
on myRemoveLastPath(myPath)
set myString to NSString's stringWithString:myPath
set removedLastPathString to myString's stringByDeletingLastPathComponent
removedLastPathString as text
end myRemoveLastPath

You need to cast to NSString with NSString's stringWithString: and cast back to Apple Script string with as text. The 's is how you can invoke functions.

One more thing is that we can support parameters to function, like this script

1
2
3
4
5
on remove:remove_string fromString:source_string	
set s_String to NSString's stringWithString:source_string
set r_String to NSString's stringWithString:remove_string
return s_String's stringByReplacingOccurrencesOfString:r_String withString:""
end remove:fromString:

How to call function inside Apple Script

Issue #88

I ‘ve been using Apple Script to for my Finder extension FinderGo. Because of sandboxing, all scripts must lie inside Application Scripts folder.

Today, I was rewriting my Xcode extension XcodeWay. Before Xcode 8, we could use Xcode plugin and all kinds of magic to make our dreams come true https://github.com/onmyway133/XcodeWay/blob/1.0/XcodeWay/Helper/FTGEnvironmentManager.m#L50. But then it does not work since Xcode Source Editor Extension was introduced. So I rewrote XcodeWay as an extension https://github.com/onmyway133/XcodeWay/releases/tag/1.1.0

Extension must run inside sandbox. If you switch App Sandbox in your XcodeWayExtensions.entitlements to NO, it won’t load. So sandbox restricts me a lot in what kinds of things I want to do. And under Xcode 9, I can’t use NSWorkspace to open Finder.

So I think I could use Apple Script too, and it worked like a charm. The only restriction is code reuse, since I only know how to run an entire script. One way is to import other Apple scripts https://stackoverflow.com/questions/2606136/import-applescript-methods-in-another-applescript but I think I will write all the functions inside 1 script, and find out how to call specific function.

By function, I also mean handler, procedure. I come across this snippet Scriptinator that pretty much inspires me, thanks to open source.

So here is my script that contains lots of functions . And here is ScriptRunner that explains how to build NSAppleEventDescriptor. Note that you need to import Carbon

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Carbon

func eventDescriptior(functionName: String) -> NSAppleEventDescriptor {
var psn = ProcessSerialNumber(highLongOfPSN: 0, lowLongOfPSN: UInt32(kCurrentProcess))
let target = NSAppleEventDescriptor(
descriptorType: typeProcessSerialNumber,
bytes: &psn,
length: MemoryLayout<ProcessSerialNumber>.size
)

let event = NSAppleEventDescriptor(
eventClass: UInt32(kASAppleScriptSuite),
eventID: UInt32(kASSubroutineEvent),
targetDescriptor: target,
returnID: Int16(kAutoGenerateReturnID),
transactionID: Int32(kAnyTransactionID)
)

let function = NSAppleEventDescriptor(string: functionName)
event.setParam(function, forKeyword: AEKeyword(keyASSubroutineName))

return event
}