A few notes on decompiling Apple Shortcuts workflows into their raw XML 'source code'.
You may also find some benefit in something like this:
- https://routinehub.co/shortcut/5256/
Shortcut Source Tool: View, convert, save source in plist or json, edit and import back to Shortcuts, review in browser
Originally posted by @0xdevalias in joshfarrant/shortcuts-js#683 (comment)
Crossposted: https://www.reddit.com/r/shortcuts/comments/13h61hv/comment/jl4be0p/
Looking a little closer at that shortcut, the main bit of functionality seems to basically be modifying the iCloud Share URL slightly:
Given a URL like this:
https://www.icloud.com/shortcuts/ABC12
It becomes:
https://www.icloud.com/shortcuts/api/records/ABC123
Then extracts the following from the JSON on that page:
- the URL for the unsigned shortcut file (which is an 'Apple binary property list' file):
fields -> shortcut -> value -> downloadURL
- the name of the shortcut workflow:
fields -> name -> value
We can then convert that binary plist file to XML or JSON using
plutil
(though when I tried the JSON format I got an error (invalid object in plist for destination format
), so might have to stick to XML):plutil -convert xml1 -e plist.xml -- the-downloaded-shortcut.plistor
plutil -convert json -e plist.json -- the-downloaded-shortcut.plist
Originally posted by @0xdevalias in joshfarrant/shortcuts-js#683 (comment)
Crossposted: https://www.reddit.com/r/shortcuts/comments/13h61hv/comment/jl4dw28/
The following are some (as yet barely verified) notes from ChatGPT:
- Private ChatGPT Convo Link: https://chatgpt.com/c/6775b854-cac4-8008-84ce-321110ba5303
On macOS, Apple Shortcuts (formerly Workflow) are stored in a combination of locations, but primarily they are managed using a SQLite database and associated plist files. Here's a breakdown of where and how they are stored at a low level:
The main storage for Shortcuts is in a SQLite database located at:
~/Library/Shortcuts/ShortcutsDatabase.db
This database contains metadata, configuration, and the structure of each shortcut. It includes tables for:
- Shortcut names
- Actions and sequences
- Variables and other contextual data
The above path doesn't seem to exist on macOS 14.5, but the following does:
~/Library/Shortcuts/Shortcuts.sqlite
Tables:
⇒ sqlite3 -readonly ~/Library/Shortcuts/Shortcuts.sqlite ".tables"
ZACCESSRESOURCEPERMISSION
ZAUTOSHORTCUTSPREFERENCES
ZCLOUDKITSYNCTOKEN
ZCOLLECTION
ZLIBRARY
ZPERSISTEDSERIALIZEDPARAMETERS
ZSHORTCUT
ZSHORTCUTACTIONS
ZSHORTCUTBOOKMARK
ZSHORTCUTICON
ZSHORTCUTQUARANTINE
ZSHORTCUTRUNEVENT
ZSMARTPROMPTPERMISSION
ZTRIGGER
ZTRIGGEREVENT
ZTRUSTEDDOMAIN
ZVCVOICESHORTCUTMANAGEDOBJECT
ZVCVOICESHORTCUTSUGGESTIONLISTMANAGEDOBJECT
ZVCVOICESHORTCUTSYNCSTATEMANAGEDOBJECT
Z_4PARENTS
Z_4SHORTCUTS
Z_METADATA
Z_MODELCACHE
Z_PRIMARYKEY
CoreData Model Hierachy:
⇒ ./extract-coredata-model-hierarchy.py ~/Library/Shortcuts/Shortcuts.sqlite
- 1: AccessResourcePermission (Table: ZACCESSRESOURCEPERMISSION)
- 2: AutoShortcutsPreferences (Table: ZAUTOSHORTCUTSPREFERENCES)
- 3: CloudKitSyncToken (Table: ZCLOUDKITSYNCTOKEN)
- 4: Collection (Table: ZCOLLECTION)
- 5: Library (Table: ZLIBRARY)
- 6: PersistedSerializedParameters (Table: ZPERSISTEDSERIALIZEDPARAMETERS)
- 7: Shortcut (Table: ZSHORTCUT)
- 8: ShortcutActions (Table: ZSHORTCUTACTIONS)
- 9: ShortcutBookmark (Table: ZSHORTCUTBOOKMARK)
- 10: ShortcutIcon (Table: ZSHORTCUTICON)
- 11: ShortcutQuarantine (Table: ZSHORTCUTQUARANTINE)
- 12: ShortcutRunEvent (Table: ZSHORTCUTRUNEVENT)
- 13: SmartPromptPermission (Table: ZSMARTPROMPTPERMISSION)
- 14: Trigger (Table: ZTRIGGER)
- 15: TriggerEvent (Table: ZTRIGGEREVENT)
- 16: TrustedDomain (Table: ZTRUSTEDDOMAIN)
- 17: VCVoiceShortcutManagedObject (Table: ZVCVOICESHORTCUTMANAGEDOBJECT)
- 18: VCVoiceShortcutSuggestionListManagedObject (Table: ZVCVOICESHORTCUTSUGGESTIONLISTMANAGEDOBJECT)
- 19: VCVoiceShortcutSyncStateManagedObject (Table: ZVCVOICESHORTCUTSYNCSTATEMANAGEDOBJECT)
⇒ sqlite3 -readonly -json ~/Library/Shortcuts/Shortcuts.sqlite "PRAGMA table_info('ZCOLLECTION');"
[{"cid":0,"name":"Z_PK","type":"INTEGER","notnull":0,"dflt_value":null,"pk":1},
{"cid":1,"name":"Z_ENT","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":2,"name":"Z_OPT","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":3,"name":"ZLASTSYNCEDHASH","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":4,"name":"ZTOMBSTONED","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":5,"name":"ZICON","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":6,"name":"ZIDENTIFIER","type":"VARCHAR","notnull":0,"dflt_value":null,"pk":0},
{"cid":7,"name":"ZNAME","type":"VARCHAR","notnull":0,"dflt_value":null,"pk":0},
{"cid":8,"name":"ZCLOUDKITFOLDERRECORDMETADATA","type":"BLOB","notnull":0,"dflt_value":null,"pk":0},
{"cid":9,"name":"ZCLOUDKITORDERINGRECORDMETADATA","type":"BLOB","notnull":0,"dflt_value":null,"pk":0},
{"cid":10,"name":"ZLASTREMOTECOLLECTIONORDERINGDATA","type":"BLOB","notnull":0,"dflt_value":null,"pk":0},
{"cid":11,"name":"ZLASTREMOTECOLLECTIONORDERINGSUBSETDATA","type":"BLOB","notnull":0,"dflt_value":null,"pk":0},
{"cid":12,"name":"ZLASTREMOTESHORTCUTORDERINGDATA","type":"BLOB","notnull":0,"dflt_value":null,"pk":0},
{"cid":13,"name":"ZLASTREMOTESHORTCUTORDERINGSUBSETDATA","type":"BLOB","notnull":0,"dflt_value":null,"pk":0},
{"cid":14,"name":"ZLASTSYNCEDENCRYPTEDSCHEMAVERSION","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":15,"name":"ZWANTEDENCRYPTEDSCHEMAVERSION","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0}]
⇒ sqlite3 -readonly -json ~/Library/Shortcuts/Shortcuts.sqlite "PRAGMA table_info('ZLIBRARY');"
[{"cid":0,"name":"Z_PK","type":"INTEGER","notnull":0,"dflt_value":null,"pk":1},
{"cid":1,"name":"Z_ENT","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":2,"name":"Z_OPT","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":3,"name":"ZLASTSYNCEDHASH","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":4,"name":"ZIDENTIFIER","type":"VARCHAR","notnull":0,"dflt_value":null,"pk":0},
{"cid":5,"name":"ZVERSION","type":"VARCHAR","notnull":0,"dflt_value":null,"pk":0},
{"cid":6,"name":"ZCLOUDKITRECORDMETADATA","type":"BLOB","notnull":0,"dflt_value":null,"pk":0},
{"cid":7,"name":"ZDATA","type":"BLOB","notnull":0,"dflt_value":null,"pk":0}]
⇒ sqlite3 -readonly -json ~/Library/Shortcuts/Shortcuts.sqlite "PRAGMA table_info('ZSHORTCUT');"
[{"cid":0,"name":"Z_PK","type":"INTEGER","notnull":0,"dflt_value":null,"pk":1},
{"cid":1,"name":"Z_ENT","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":2,"name":"Z_OPT","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":3,"name":"ZACTIONCOUNT","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":4,"name":"ZHASOUTPUTFALLBACK","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":5,"name":"ZHASSHORTCUTINPUTVARIABLES","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":6,"name":"ZHIDDENFROMWIDGET","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":7,"name":"ZLASTSYNCEDHASH","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":8,"name":"ZRECEIVESONSCREENCONTENT","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":9,"name":"ZREMOTEQUARANTINESTATUSVALUE","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":10,"name":"ZRUNEVENTSCOUNT","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":11,"name":"ZSYNCHASH","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":12,"name":"ZTOMBSTONED","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":13,"name":"ZTRIGGERCOUNT","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":14,"name":"ZACTIONS","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":15,"name":"ZCONFLICTOF","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":16,"name":"ZICON","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":17,"name":"ZQUARANTINE","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":18,"name":"ZCREATIONDATE","type":"TIMESTAMP","notnull":0,"dflt_value":null,"pk":0},
{"cid":19,"name":"ZLASTRUNEVENTDATE","type":"TIMESTAMP","notnull":0,"dflt_value":null,"pk":0},
{"cid":20,"name":"ZMODIFICATIONDATE","type":"TIMESTAMP","notnull":0,"dflt_value":null,"pk":0},
{"cid":21,"name":"ZACTIONSDESCRIPTION","type":"VARCHAR","notnull":0,"dflt_value":null,"pk":0},
{"cid":22,"name":"ZASSOCIATEDAPPBUNDLEIDENTIFIER","type":"VARCHAR","notnull":0,"dflt_value":null,"pk":0},
{"cid":23,"name":"ZGALLERYIDENTIFIER","type":"VARCHAR","notnull":0,"dflt_value":null,"pk":0},
{"cid":24,"name":"ZLASTMIGRATEDCLIENTVERSION","type":"VARCHAR","notnull":0,"dflt_value":null,"pk":0},
{"cid":25,"name":"ZLASTSAVEDONDEVICENAME","type":"VARCHAR","notnull":0,"dflt_value":null,"pk":0},
{"cid":26,"name":"ZMINIMUMCLIENTVERSION","type":"VARCHAR","notnull":0,"dflt_value":null,"pk":0},
{"cid":27,"name":"ZNAME","type":"VARCHAR","notnull":0,"dflt_value":null,"pk":0},
{"cid":28,"name":"ZPHRASE","type":"VARCHAR","notnull":0,"dflt_value":null,"pk":0},
{"cid":29,"name":"ZSOURCE","type":"VARCHAR","notnull":0,"dflt_value":null,"pk":0},
{"cid":30,"name":"ZWORKFLOWID","type":"VARCHAR","notnull":0,"dflt_value":null,"pk":0},
{"cid":31,"name":"ZWORKFLOWSUBTITLE","type":"VARCHAR","notnull":0,"dflt_value":null,"pk":0},
{"cid":32,"name":"ZCLOUDKITRECORDMETADATA","type":"BLOB","notnull":0,"dflt_value":null,"pk":0},
{"cid":33,"name":"ZIMPORTQUESTIONSDATA","type":"BLOB","notnull":0,"dflt_value":null,"pk":0},
{"cid":34,"name":"ZINPUTCLASSESDATA","type":"BLOB","notnull":0,"dflt_value":null,"pk":0},
{"cid":35,"name":"ZNOINPUTBEHAVIORDATA","type":"BLOB","notnull":0,"dflt_value":null,"pk":0},
{"cid":36,"name":"ZOUTPUTCLASSESDATA","type":"BLOB","notnull":0,"dflt_value":null,"pk":0},
{"cid":37,"name":"ZLASTSYNCEDENCRYPTEDSCHEMAVERSION","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":38,"name":"ZWANTEDENCRYPTEDSCHEMAVERSION","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":39,"name":"ZDISABLEDONLOCKSCREEN","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":40,"name":"ZREMOTEQUARANTINEHASH","type":"BLOB","notnull":0,"dflt_value":null,"pk":0},
{"cid":41,"name":"ZSHOULDAUTOUPDATEASSOCIATEDAPPBUNDLEIDENTIFIER","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":42,"name":"ZHIDDENFROMLIBRARYANDSYNC","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0}]
⇒ sqlite3 -readonly -json ~/Library/Shortcuts/Shortcuts.sqlite "PRAGMA table_info('ZSHORTCUTACTIONS');"
[{"cid":0,"name":"Z_PK","type":"INTEGER","notnull":0,"dflt_value":null,"pk":1},
{"cid":1,"name":"Z_ENT","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":2,"name":"Z_OPT","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":3,"name":"ZSHORTCUT","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":4,"name":"ZDATA","type":"BLOB","notnull":0,"dflt_value":null,"pk":0}]
⇒ sqlite3 -readonly -json ~/Library/Shortcuts/Shortcuts.sqlite "PRAGMA table_info('ZSHORTCUTBOOKMARK');"
[{"cid":0,"name":"Z_PK","type":"INTEGER","notnull":0,"dflt_value":null,"pk":1},
{"cid":1,"name":"Z_ENT","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":2,"name":"Z_OPT","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":3,"name":"ZIDENTIFIER","type":"VARCHAR","notnull":0,"dflt_value":null,"pk":0},
{"cid":4,"name":"ZPATH","type":"VARCHAR","notnull":0,"dflt_value":null,"pk":0},
{"cid":5,"name":"ZBOOKMARKDATA","type":"BLOB","notnull":0,"dflt_value":null,"pk":0}]
⇒ sqlite3 -readonly -json ~/Library/Shortcuts/Shortcuts.sqlite "PRAGMA table_info('ZTRIGGER');"
[{"cid":0,"name":"Z_PK","type":"INTEGER","notnull":0,"dflt_value":null,"pk":1},
{"cid":1,"name":"Z_ENT","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":2,"name":"Z_OPT","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":3,"name":"ZENABLED","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":4,"name":"ZNOTIFICATIONLEVEL","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":5,"name":"ZSHOULDNOTIFY","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":6,"name":"ZSHOULDPROMPT","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":7,"name":"ZSOURCE","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":8,"name":"ZRUNEVENTS","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":9,"name":"ZSHORTCUT","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":10,"name":"ZIDENTIFIER","type":"VARCHAR","notnull":0,"dflt_value":null,"pk":0},
{"cid":11,"name":"ZDATA","type":"BLOB","notnull":0,"dflt_value":null,"pk":0},
{"cid":12,"name":"ZEDITABLESHORTCUT","type":"INTEGER","notnull":0,"dflt_value":null,"pk":0},
{"cid":13,"name":"ZSELECTEDENTRYMETADATA","type":"BLOB","notnull":0,"dflt_value":null,"pk":0}]
This shows a few of the relevant'ish looking human readable columns:
⇒ sqlite3 -readonly -json ~/Library/Shortcuts/Shortcuts.sqlite "SELECT Z_PK, ZNAME, ZWORKFLOWSUBTITLE, ZACTIONCOUNT, ZTRIGGERCOUNT, ZACTIONS, ZSOURCE, ZWORKFLOWID FROM ZSHORTCUT;"
[
// ..snip..
{
"Z_PK": 15,
"ZNAME": "Sleep (20min)",
"ZWORKFLOWSUBTITLE": "5 actions",
"ZACTIONCOUNT": 5,
"ZTRIGGERCOUNT": 0,
"ZACTIONS": 15,
"ZSOURCE": null,
"ZWORKFLOWID": "66DF3D2D-0FCF-471E-84D2-597A501648D4"
},
// ..snip..
]
This is similar to above, but will also JOIN
with ZSHORTCUTACTIONS
to link the ZDATA
field as well (which appears to be a bplist
):
⇒ sqlite3 -readonly -json ~/Library/Shortcuts/Shortcuts.sqlite "
SELECT
ZSHORTCUT.Z_PK,
ZSHORTCUT.ZNAME,
ZSHORTCUT.ZWORKFLOWSUBTITLE,
ZSHORTCUT.ZACTIONCOUNT,
ZSHORTCUT.ZTRIGGERCOUNT,
ZSHORTCUT.ZACTIONS,
ZSHORTCUT.ZSOURCE,
ZSHORTCUT.ZWORKFLOWID,
ZSHORTCUTACTIONS.ZDATA
FROM ZSHORTCUT
LEFT JOIN ZSHORTCUTACTIONS ON ZSHORTCUT.ZACTIONS = ZSHORTCUTACTIONS.Z_PK;"
This is similar to above, but will also attempt to parse the ZDATA
bplist
with python's plistlib
:
⇒ sqlite3 -readonly -json ~/Library/Shortcuts/Shortcuts.sqlite "
SELECT
ZSHORTCUT.Z_PK,
ZSHORTCUT.ZNAME,
ZSHORTCUT.ZWORKFLOWSUBTITLE,
ZSHORTCUT.ZACTIONCOUNT,
ZSHORTCUT.ZTRIGGERCOUNT,
ZSHORTCUT.ZACTIONS,
ZSHORTCUT.ZSOURCE,
ZSHORTCUT.ZWORKFLOWID,
hex(ZSHORTCUTACTIONS.ZDATA) AS ZDATA_HEX
FROM ZSHORTCUT
LEFT JOIN ZSHORTCUTACTIONS ON ZSHORTCUT.ZACTIONS = ZSHORTCUTACTIONS.Z_PK;" | python3 -c "
import sys, json, plistlib, binascii
# Custom JSON encoder for unsupported types
class CustomJSONEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, bytes):
return obj.hex() # Convert bytes to a hexadecimal string
return super().default(obj)
# Load JSON from stdin
data = json.load(sys.stdin)
# Decode ZDATA_HEX
for row in data:
if row.get('ZDATA_HEX'):
try:
# Convert hex to bytes
row['ZDATA'] = plistlib.loads(binascii.unhexlify(row['ZDATA_HEX']))
del row['ZDATA_HEX'] # Remove the hex version
except Exception as e:
row['ZDATA'] = f'Decoding Error: {e}'
# Print the modified JSON
print(json.dumps(data, indent=2, cls=CustomJSONEncoder))
"
Which results in output like this:
[
// ..snip
{
"Z_PK": 15,
"ZNAME": "Sleep (20min)",
"ZWORKFLOWSUBTITLE": "5 actions",
"ZACTIONCOUNT": 5,
"ZTRIGGERCOUNT": 0,
"ZACTIONS": 15,
"ZSOURCE": null,
"ZWORKFLOWID": "66DF3D2D-0FCF-471E-84D2-597A501648D4",
"ZDATA": [
{
"WFWorkflowActionIdentifier": "is.workflow.actions.number",
"WFWorkflowActionParameters": {
"UUID": "4A620A3D-E6A4-48AA-8191-A5A29F77B327",
"WFNumberActionNumber": 1200.0
}
},
{
"WFWorkflowActionIdentifier": "is.workflow.actions.delay",
"WFWorkflowActionParameters": {
"WFDelayTime": {
"Value": {
"OutputUUID": "4A620A3D-E6A4-48AA-8191-A5A29F77B327",
"Type": "ActionOutput",
"OutputName": "Number"
},
"WFSerializationType": "WFTextTokenAttachment"
}
}
},
{
"WFWorkflowActionIdentifier": "is.workflow.actions.pausemusic",
"WFWorkflowActionParameters": {}
},
{
"WFWorkflowActionIdentifier": "is.workflow.actions.airplanemode.set",
"WFWorkflowActionParameters": {}
},
{
"WFWorkflowActionIdentifier": "is.workflow.actions.timer.start",
"WFWorkflowActionParameters": {}
}
]
},
// ..snip
]
Individual shortcuts are also stored as serialized plist (property list) files in the directory:
~/Library/Shortcuts/Shortcuts
Each shortcut has a unique identifier, and its details are saved in
.shortcut
files. These files are a binary or XML representation of the shortcut's workflow.
On macOS 14.5, that directory doesn't seem to exist, but if we go up one directory, these are the files I can see in that folder:
⇒ ls -l ~/Library/Shortcuts
SecuredPreferences.plist
ShareSheetState.plist
Shortcuts.sqlite
Shortcuts.sqlite-shm
Shortcuts.sqlite-wal
Spotlight.dat
Temporary/
ssh/
⇒ ls -l ~/Library/Shortcuts/Temporary
com.apple.WorkflowKit.BackgroundShortcutRunner/
com.apple.WorkflowUI.CatalystContentExtension/
com.apple.shortcuts/
com.apple.shortcuts.Run-Workflow/
com.apple.shortcuts.ThumbnailExtension/
com.apple.shortcuts.events/
com.apple.siriactionsd/
That said, based on the exploration of the sqlite DB earlier.. it looks like we can get the shortcut actions from the ZSHORTCUT
/ ZSHORTCUTACTIONS
tables.
If iCloud syncing is enabled, shortcuts may also be stored in the iCloud directory:
~/Library/Mobile Documents/iCloud~is~workflow/Shortcuts
This allows synchronization across Apple devices, and the format mirrors the local storage with plist files and associated metadata.
On macOS 14.5, that folder doesn't seem to exist, but these ones did (though they seem to be empty?):
⇒ ls -la ~/Library/Mobile\ Documents | grep -E 'workflow|shortcut'
iCloud~com~apple~shortcuts~runtime/
iCloud~is~workflow~my~workflows/
⇒ fd . ~/Library/Mobile\ Documents/iCloud\~com\~apple\~shortcuts\~runtime
/Users/devalias/Library/Mobile Documents/iCloud~com~apple~shortcuts~runtime/Documents/
⇒ fd . ~/Library/Mobile\ Documents/iCloud\~is\~workflow\~my\~workflows
/Users/devalias/Library/Mobile Documents/iCloud~is~workflow~my~workflows/Documents/
Additional metadata, such as user preferences and categories, is often stored in other plist files in:
~/Library/Preferences/com.apple.shortcuts.plist
On macOS 14.5, this file seems to exist, but it doesn't look super interesting:
⇒ plutil -convert xml1 -o - ~/Library/Preferences/com.apple.shortcuts.plist
<?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>CKPerBootTasks</key>
<array>
<string>CKAcccountInfoCacheReset</string>
</array>
<key>CKStartupTime</key>
<integer>1731387059</integer>
<key>MainViewLayoutMode</key>
<string>grid</string>
<key>NSNavLastRootDirectory</key>
<string>~/Desktop</string>
<key>NSNavPanelExpandedSizeForSaveMode</key>
<string>{800, 448}</string>
<key>NSNavPanelExpandedStateForSaveMode</key>
<true/>
<key>NSWindow Frame Main Window</key>
<string>765 279 1000 678 0 0 1792 1095 </string>
<key>NSWindow Frame NSNavPanelAutosaveName</key>
<string>1320 590 800 364 0 0 3440 1415 </string>
<key>NSWindow Frame NSSpellCheckerSubstitutionsPanel2</key>
<string>-116 1527 428 137 -808 1120 3440 1415 </string>
<key>wdsCacheCleanupState</key>
<dict>
<key>com.apple.shortcuts</key>
<true/>
</dict>
</dict>
</plist>
- SQLite Database: Used for managing and querying shortcuts metadata efficiently.
- Plist Files: Contain the actual logic and structure of the shortcuts.
- iCloud Sync: Optional storage for cloud syncing.
- Sandboxing: The Shortcuts app adheres to macOS sandboxing rules, restricting access to its files unless explicitly granted.
If you want to explore or modify these files, you should do so cautiously and make backups, as manual changes could corrupt the database or shortcuts.
- https://github.com/joshfarrant/shortcuts-js
-
A JavaScript iOS 12 Shortcuts creator
- https://shortcuts.fun/
-
Shortcuts JS 🧞♂️ Create Apple Shortcuts using JavaScript
-
-
- https://github.com/sebj/iOS-Shortcuts-Reference
-
Shortcuts File Format Reference
-
Reference documentation for the iOS Shortcuts app file structure
-
- https://zachary7829.github.io/blog/shortcuts/
-
Deep Into Shortcuts
- https://zachary7829.github.io/blog/shortcuts/fileformat
-
Shortcuts File Format Documentation
-
How shortcuts are stored On iOS 13, shortcuts are stored in
/var/mobile/Library/Shortcuts/Shortcuts.realm
. However, iOS 14 changes this to be inShortcuts.sqlite
-
On iOS 12/13/14 devices, a shortcut that's stored on a device can be exported as an unsigned
.shortcut
file by using the Get My Shortcuts action (You can use a Save File action to save the output). -
iOS 15 is slightly different, however: you can't export shortcuts on device as unsigned shortcuts using Get My Shortcuts, only signed. (Shortcuts in iOS 15 are signed with Apple Encrypted Archives - learn more about them here: https://man.cameronkatri.com/macOS/aea). You can, however, get a unsigned
.shortcut
from the iCloud API. Let's upload a shortcut to iCloud, and imagine our link ishttps://www.icloud.com/shortcuts/77dfe31578ac4f6fb084ebb418b34a49
. Change/shortcuts/
to/shortcuts/api/records/
(https://www.icloud.com/shortcuts/api/records/77dfe31578ac4f6fb084ebb418b34a49
). The value forfields.shortcut.value.downloadURL
should be the URL for the unsigned.shortcut
(Note: If you opened in Safari, change\/
to/
in the URL). After getting the unsigned.shortcut
, rename this to a.plist
and you should be able to easily open this in Xcode (or, use set name to rename to something.plist with Do Not Include File Extension on, and Get Text from that). -
Importing On iOS 12, you could just import
.shortcuts
from files no issue. However, this was disabled on iOS 13. It's still fairly easy to work around this though - you could have a shortcut that passes a.shortcut
file into the Get Link to File action, and it should generate an iCloud link to this.shortcut
in which you can import. Similar behavior on iOS 14. (Fun fact: While this is disabled, the code for it is still there: you can actually make a backup, edit a boolean in aplist
, restore from said backup and you can import.shortcuts
again. Apple actually accidentally enabled it again in iOS 14b1, though quickly realized their mistake and disabled it again.)On iOS 15, however, Apple introduced shortcuts signing, as well as removed the special treatment for Get Link to File regarding
.shortcut
/.wflow
files. This means that you need to sign a shortcut file if you want to import it on an iOS 15 device - and no, you can't do it on device. (Note: There was a way to bypass signing and import unsigned shortcuts files on iOS 15b1, but it was quickly patched.) You need either a Mac or an iOS 12/13/14 device to sign said shortcut file. (To sign on macOS - use the shortcuts CLI tool. To sign on an iOS 12/13/14 device, have a shortcut get the iOS 15 shortcut you want to sign, and use the Get Link to File action to upload to iCloud and sign it. The link to the signed shortcut file should be infields.signedShortcut.value.downloadURL
. However, you could set up a shortcuts signing server if you have a Mac if you really want to - but be aware that if someone uploads a malicious shortcut, Apple could ban it.
-
-
- Gist Announcement Tweet: https://twitter.com/_devalias/status/1660516786826264585
- https://shortcuts.fun/
-
Create Apple Shortcuts using JavaScript
- https://github.com/joshfarrant/shortcuts-js
-
A JavaScript iOS 12 Shortcuts creator
- joshfarrant/shortcuts-js#683
-
Looking for new maintainers
-
-
-
- https://www.reddit.com/r/shortcuts/comments/13h61hv/gpt4_can_write_shortcuts/
-
GPT4 can write shortcuts
-
tanks !