iPhone,  JAMF,  Java

Automate and Distribute Apple Shortcuts

Shortcuts (prior to being acquired by Apple it was called Workflow) is a free app for iOS that provides automated actions, similar to AppleScript or Automator workflows. Users can make Shortcuts or download Shortcuts other people have made.

Shortcuts are javascript wrapped in a binary property list. You can easily create a Shortcut by opening the Shortcuts app and tapping on Create Shortcut.

You’ll then see a list of apps that can be used with Shortcuts. for this example we’ll use Email Address. Tap here and you’ll be prompted to Allow Access to Email addresses from the Shortcut (you’ll be prompted twice actually). Then provide an email address.

Select one and then select a scripting action.

Actually we don’t want to use an address there, we just wanted you to see the privacy protection so let’s remove that step by tapping the X. Instead, for this example, we’ll use Get URLs from Input. Then Get Contents of Webpage. Then Send Message. And we’ll select Magic Variable. Then save. Running the workflow then sends the content of URLs from input to the person you select in the send message.

You can then share a shortcut via iMessage, to an iCloud account, via Airdrop, etc. Now, let’s go ahead and look inside a Shortcut. To do so, copy the Shortcut to a Mac and run a cat on it.

cat /Users/ce/Downloads/kryptedtest.shortcut 

You can see some of the stuff, but it looks weird. That’s because it’s a binary plist. Let’s convert that to an xml1 plist using plutil:

plutil -convert xml1 /Users/ce/Downloads/kryptedtest.shortcut 

Now that we’ve converted it, let’s read the contents again. This time, we see a bunch of xml. The following is a really simple example:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>WFWorkflowActions</key>
	<array>
		<dict>
			<key>WFWorkflowActionIdentifier</key>
			<string>is.workflow.actions.runjavascriptonwebpage</string>
			<key>WFWorkflowActionParameters</key>
			<dict>
				<key>WFJavaScript</key>
				<string>window.open ('https://www.krypted.com','_self',false)</string>
			</dict>
		</dict>
	</array>
	<key>WFWorkflowClientRelease</key>
	<string>2.0</string>
	<key>WFWorkflowClientVersion</key>
	<string>700</string>
	<key>WFWorkflowIcon</key>
	<dict>
		<key>WFWorkflowIconGlyphNumber</key>
		<integer>59801</integer>
		<key>WFWorkflowIconImageData</key>
		<data>
		</data>
		<key>WFWorkflowIconStartColor</key>
		<integer>4292093695</integer>
	</dict>
	<key>WFWorkflowImportQuestions</key>
	<array/>
	<key>WFWorkflowInputContentItemClasses</key>
	<array>
		<string>WFSafariWebPageContentItem</string>
	</array>
	<key>WFWorkflowTypes</key>
	<array>
		<string>NCWidget</string>
		<string>WatchKit</string>
		<string>ActionExtension</string>
	</array>
</dict>
</plist>

This basically steps through the workflow actions. And while Apple has long dissed on some Java, the WFJavaScript key is where the magic happens as that’s where we’re able to insert arbitrary javascript. Now, let’s say you wanted to customize this for a pool of users for distribution. Enter https://github.com/krypted/shortcutter. This little yucky brand unix script accepts a .js file as input and it will spit out a pretty basic output.

python3 shortcutter.py [-h] [-o OUTPUT] -i INPUT

As an example:

python3 shortcutter.py -i somefile.js -o outputName

You can check the script options with -h, the result will be the next:

optional arguments: -h, --help show this help message and exit -o OUTPUT, --output OUTPUT The name of your plist file, default = "java_script_file.shortcut"

required named arguments: -i INPUT, --input INPUT The java script file to insert into the plist file

Obviously, there are a lot of different workflow items to choose from. And because arbitrary apps you’ve installed might have their own actions, you really can’t programmatically account for all of them. However, the plistlib in the script is super-flexible and if Shortcuts become more of a thing we’d benefit from mapping all of the built-ins out and putting them into the script as flags. It’s not difficult, just time consuming. So that’s how they work and how you can scriptify the scriptybits.