Repository navigation
Render Swift collections as AppleScript lists and records - #6
Merged
Merged
Conversation
An Array or Dictionary passed as a variable used to fall through to String(describing:) and reach the script as text such as ["a", "b"], which left callers hand-building AppleScript source and wrapping it in AppleScriptRawValue — taking on escaping every element themselves. Arrays now render as lists and dictionaries as records, with elements going back through substitution(for:) so escaping and literal rendering stay in one place and nesting works. Text supplies its own quotes inside a collection, since the script provides none there, unlike around a placeholder. Record keys that are not plain AppleScript identifiers, reserved words included, are vertical-bar quoted; identifiers are left bare so a term the target application defines keeps its meaning. Keys are sorted so the same dictionary always renders the same source. Closes #1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Also drops the README roadmap line pointing at #1, now that it ships. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this changes
Swift collections are rendered as their AppleScript equivalents on substitution: an
Arraybecomes a list, aDictionarybecomes a record, and the two nest, so an[[String: Any]]arrives as a list of records. Elements go back throughsubstitution(for:), so escaping and literal rendering stay in one place. Fixes #1.Tuples were left out of scope: they need
Mirror-based reflection to iterate, which would be the only reflection in the package, and arrays and dictionaries cover the real call sites. Worth a separate issue if anyone asks for them.Why
substitution(for:)handledString,Int,Double,BoolandAppleScriptRawValue; anything else fell through toString(describing:), so a Swift array reached the script as the text["a", "b"]. Passing a list meant hand-building AppleScript source, wrapping it inAppleScriptRawValue, and taking on escaping every element yourself — the one place the package handed back a job it should have been doing.Notes for the reviewer
Text quoting depends on position. A placeholder is written inside the script's own quotes (
"$key"), so a substituted string must not add a second pair; an element inside{…}has no quotes around it and must supply its own. Hence thequotingTextparameter onsubstitution(for:), set only bylist(for:)andrecord(for:).Record keys. AppleScript record keys are identifiers, not strings. A key that reads as a plain identifier is left bare, so a term the target application defines keeps its meaning —
{name:"Roger"}sets the app'snameproperty, while{|name|:"Roger"}would define an unrelated one. Keys with spaces or punctuation, and AppleScript's reserved words, are vertical-bar quoted, with\and|escaped inside. Both escapes were verified againstosascript; reserved words were verified to be rejected bare.Behaviour change, no API change. Nothing stops compiling on the Swift side, but a script written against the old
String(describing:)text has to be updated. One sharp edge, called out in the changelog: a collection in a quoted placeholder now renders"{"a", "b"}", an AppleScript syntax error rather than silently wrong text — loud, and at compile time.SetandDatastill take the old describe-and-escape path, so nothing beyond arrays and dictionaries shifted.Ordering. Dictionary keys are sorted on the key as written, so quoting doesn't reorder anything and the same dictionary always renders the same source.
How it was tested
swift buildandswift testpass — 30 tests, six new unit tests for lists, records, nesting, empty collections, key quoting and bridged dictionaries. One of the new tests runs a generated list of records throughosascriptand readsnameand|first name|back out of it, so the rendering is proven to compile rather than just to match a string.Examples/AppleScripts.swiftwas built as a consumer package the way CI does it.SwiftLint could not run locally (
sourcekitdInProcfails to load on this machine), so the strict lint job is unverified here.osascriptonly.Checklist
swift buildandswift testpass.///doc comment describing purpose, parameters and return value.🤖 Generated with Claude Code