Commands
// TO DO: this chapter is still rough, and there's little commonality between ObjC and Swift text; the ObjC API is also not finalized and may change slightly/drastically in future
Syntax
For convenience, AppleEventBridge makes application commands available as methods on every object specifier. (Note: due to the limitations of aete-based terminology, the user must determine for themselves which commands can operate on a particular reference. Some applications document this information separately.) All application commands have the same basic structure: a single, optional direct parameter, followed by zero or more named parameters specific to that command, followed by zero or more event attributes that determine how the Apple event is processed:
func commandName(directParameter: AnyObject = AEBNoParameter,
namedParameter1: AnyObject = AEBNoParameter,
namedParameter2: AnyObject = AEBNoParameter,
...
returnType: AEBReturnType? = nil,
waitReply: Bool? = nil,
withTimeout: NSTimeInterval? = nil,
considering: AEBConsiderIgnoreType? = nil,
ignoring: AEBConsiderIgnoreType? = nil) throws -> AnyObject!
directParameter:
-- An application command can have either zero or one direct parameters. The application's dictionary indicates which commands take a direct parameter, and if it is optional or required.namedParameterN:
An application command can have zero or more named parameters. The application's dictionary describes the named parameters for each command, and if they are optional or required.returnType:
-- Some applications may allow the return value's type to be specified for certain commands (typicallyget
). For example, the Finder'sget
command returns filesystem references as alias objects if the resulttype isFIN.alias
. [TO DO: this is preliminary and subject to change]waitReply:
-- Iftrue
(the default), the command will block until the application sends a reply or the request times out. Iffalse
, it will return as soon as the request is sent, ignoring any return values or application errors.withTimeout:
-- The number of seconds to wait for the application to reply before raising a timeout error. The default timeout (AEBDefaultTimeout
) is 120 seconds but this can be changed if necessary; useAEBNoTimeout
to wait indefinitely. For example, a longer timeout may be needed to prevent a timeout error occurring during a particularly long-running application command. Note: due to a quirk in the Apple Event Manager API, timeout errors may be reported as either error -1712 (the Apple event timed out) or -609 (connection invalid, which is also raised when an application unexpectedly quits while handling a command).considering:
/ignoring:
-- Some applications may allow the client to specify text attributes that should be considered or ignored when performing string comparisons, e.g. when resolving by-test references. When specifying the attributes to ignore, the list should contain zero or more of the following enumerators:XX.case
,XX.diacriticals
,XX.numericStrings
,XX.hyphens
,XX.punctuation
,XX.whitespace
[TO DO: these are standard terms so should eventually be defined on AEBSymbol base class, allowingAEB.case
, etc to be used across all apps, but for now must use the glue's own prefix code, e.g.TED.case
]. Note that most applications currently ignore this setting and always use the default behaviour, which is to ignore case but consider everything else.
Examples
// tell application "TextEdit" to activate
TextEdit().activate()
// tell application "TextEdit" to open fileList
TextEdit().open(fileList)
// tell application "Finder" to get version
Finder().version.get()
// tell application "Finder" to set name of file "foo.txt" of home to "bar.txt"
Finder().home.files["foo.txt"].name.set(to: "bar.txt")
// tell application "TextEdit" to count (text of first document) each paragraph
TextEdit().documents.first.text.count(each: TED.paragraph)
// tell application "TextEdit" to make new document at end of documents
TextEdit().documents.end.make(new: TED.document)
// tell application "Finder" to get items of home as alias list
Finder().home.items.get(returnType: FIN.alias)
Special cases
The following special-case behaviours are implemented for convenience:
Commands that take an object specifier as a direct parameter may be written in the following form:
objSpec.command(namedParameter1:, namedParameter2:, ...) The conventional form is also supported should you ever wish (or need) to use it: appObj.command(objSpec, namedParameter1:, namedParameter2:, ...)
The two forms are equivalent (AEBSpecifier
converts the first form to the second behind the scenes) although the first form is preferred for conciseness.
If a command that already has a direct parameter is called on an object specifier, i.e.:
objSpec.command(directParameter:, ...)
the object specifier upon which it is called will be packed as the Apple event's "subject" attribute (keySubjectAttr
).
If the
make
command is called on an insertion location specifier (before
/after
/beginning
/end
), AEB will pack that specifier as the Apple event'sat:
parameter if it doesn't already have one; i.e.:insertionLoc.make(new: className)
is equivalent to:
appObj.make(new: className, at: insertionLoc)
If the
make
command is called on an object specifier, AEB will pack that specifier as the Apple event's "subject" attribute. Be aware that some applications may not handle this attribute correctly, in which case the specifier should be passed via themake
command'sat:
parameter.
Command errors
If a command fails due to an error raised by the target application or Apple Event Manager, or if a given parameter or attribute was not of a supported type, an NSError
is thrown. AppleEventBridge errors have the domain AEBErrorDomain
, an error code that is typically an OSStatus
value or custom value defined by the target application, and a userInfo
dictionary containing a standard NSLocalizedDescription
key containing the error description string, plus zero or more of the following AEB-defined keys:
Standard Apple event/OSA error information:
AEBErrorNumber
– the error code (Int
); this is the same asNSError.code
AEBErrorMessage
– the error message (String
) provided by the application, if any, otherwise a default description if the error number is a standard AE error codeAEBErrorBriefMessage
– short version of the above; not normally used by applications, but included here for completenessAEBErrorExpectedType
– if a coercion error (-1700) occurred, anAEBSymbol
describing the type of value that was requiredAEBErrorOffendingObject
– the parameter (AnyObject
) that caused the error, where relevant
Additional error information:
AEBErrorFailedCommandDescription
– the source codeString
representation of the failed commandAEBErrorFailedAEMEvent
– the underlyingAEMEvent
instance that was constructed by the Swift glue
Note to AppleScript users
Unlike AppleScript, which implicitly sends a get
command to any unresolved application object references at the end of evaluating an expression, AEB only resolves a reference when it receives an appropriate command. For example:
let o = TextEdit().documents
is not the same as:
set o to documents of application "TextEdit"
even though the two statements may look equivalent. In the Swift example, the value assigned to o
is an instance of TEDSpecifier
, TextEdit(name:"/Applications/TextEdit.app").documents
, i.e. an object specifier. Whereas, in the AppleScript example, the evaluating the documents of application "TextEdit"
expression not only constructs the same specifier, it also automatically sends a get
event to the target application in order to retrieve the specified data, then assigns the result of that request to o
:
set o to documents of application "TextEdit"
o
--> {document "Untitled" of application "TextEdit", document "Untitled 2" of application "TextEdit"}
This "implicit get
" behavior is built directly into the AppleScript interpreter itself, and automatically applied to any specifier literal that does not already appear as a parameter to an application command, as a tell
block target, or as the sole operand to AppleScript's' a reference to
operator:
set o to a reference to documents of application "TextEdit"
o
--> every document of application "TextEdit"
In contrast, AEB has no invisible "magic" behaviors attempting to infer your actual intent: it only ever sends an Apple event when you explicitly instruct it to do so:
let o = TextEdit().documents.get()
print(o)
// [TextEdit(...).documents["Untitled"], TextEdit(...).documents["Untitled 2"]]
New users coming from AppleScript or OO language backgrounds may find this unintuitive at first, but AEB's clean separation between query construction and event dispatch ensures AEB's behavior is completely straightforward and predictable, and avoids the hidden gotchas that can bite AppleScript users in various unexpected and confusing ways.