Post

Replies

Boosts

Views

Activity

Reply to Controlling Music (iTunes) from a Mac OS Swift app.
You will need to use Apple event IPC to control Music.app. Options: • ScriptingBridge.framework – defective by design and effectively unsupported and abandoned; usually mostly works for trivial stuff, but breaks on a lot of tasks that work perfectly in AppleScript and generally not worth wasting time on • AppleScript-ObjC bridge – allows you to wrap your AppleScript handlers as NSObject subclasses and call them directly from your Swift/ObjC code. Bridging numerical types is a little fiddly (you have to use NSNumber, not Bool/Int/Double primitives) and block-based APIs are unsupported, but this is by far the least awful option and the one I recommend. Example project here: github.com/hhas/Swift-AppleScriptObjC (There is also SwiftAutomation, which unlike Apple’s ScriptingBridge actually works right, but I haven’t used or maintained it in ages and don’t provide any support so for the recklessly brave only.)
Jan ’22
Reply to JXA to order Reminders
Yep, always test first in AppleScript to see if it works there. I can confirm there’s a bug in Reminders.app on 10.14; I expect it’s the same on 10.15. The following move command removes the specified element[s] from the collection but fails to insert it at the new position: - (manually create some reminders named "test-1", "test-2", "test-3", etc) tell application "Reminders" move reminder "test-2" to beginning of reminders end tell The app returns error -10000, “AppleEvent handler failed”, and the reminder named "test-2" disappears entirely. That’s a significant (data-destroying) bug. Ditto for: move reminder "test-2" to end of reminders move reminder "test-3" to before reminder "test-2" move reminder "test-1" to after reminder "test-3" (There is an art to implementing reliable move and duplicate commands that don’t **** up with “off-by-one” and other bugs. Alas, the Cocoa Scripting framework upon which most apps’ scripting support is built is somewhat clunky, and app developers rarely test their scripting support as thoroughly as they should.) You can try filing a bug report on Reminders.app, though given Apple has all but abandoned AppleScript automation there’s no guarantee it’ll be fixed any time soon, or ever. (Note that JXA’s broken reference forms are a completely unrelated defect, and since JXA is completely abandoned I wouldn’t even bother filing bug reports on that. I already tried, and it was a waste of my time. Again, stick to AppleScript to minimise your pain.)
Jun ’20
Reply to JXA to order Reminders
Sorry, but you’re SOOL. Amongst their many frustrating defects, neither JXA nor Scripting Bridge allow you to construct insertion location specifiers, so it’s impossible to express commands such as move X to beginning/end of Y or move X to before/after Y. (There’s no technical reason why JXA and SB should be so crippled: appscript, SwiftAutomation, and nodeautomation all implemented AppleScript-quality AE bridges just fine. The Mac Automation team just botched them is all. And now that department’s disbanded, JXA and SB are dead tech walking.) Stick to AppleScript; the language may be awful but it’s the only nominally supported solution that speaks Apple events right.
Jun ’20
Reply to AppleScript ensuring compatibility for a program with multiple versions
The absolute awfulness of GUI Scripting as a solution is orthogonal to the choice of language used to write it. I’m pretty sure if Ableton Live provided a proper Apple event interface the OP would be using that to control it; as they aren’t, it’s fair to assume they’re using GUI Scripting not because it’s the best or the worst solution available to them but because it’s the ONLY solution there is.
May ’20
Reply to AppleScript ensuring compatibility for a program with multiple versions
This is nonsense. AppleScript is perfectly appropriate to OP’s task, and the only supported option that speaks Apple events right. Anything else is an unnecessary exercise in pain. (Yes, there are lower-level Accessibility APIs, but those aren’t any easier to use than GUI Scripting.)OP has two problems. First, using GUI Scripting to automate another application is notoriously fragile, not very portable, and subject to macOS security blocks. But I’m going to assume the Ableton Live app does not provide any sort of API making this unavoidable. Second, OP needs to activate the Ableton Live Intro/Standard/Suite app so that it receives the key events sent via GUI Scripting. If all versions of the app have the same CFBundleIdentifier then just use that, e.g.:tell application id "com.example.foo" to activateIf they have different bundle identifiers then I’d suggest something like the following handler, which attempts to launch each app in turn:on launchApp() repeat with aRef in {"com.example.foo.suite", "com.example.foo.standard", "com.example.foo.intro"} try activate application id aRef return on error number -1728 -- app not found end try end repeat error number -1728 -- fail if none of the supported apps were found end launchApp launchApp()Since the only command used here is `activate`, there’s no need to worry about compiling application-specific terminology. Dealing with all the other pitfalls of GUI Scripting is left as an exercise to OP and their users, natch.
May ’20
Reply to script editor
This question has nothing to do with Script Editor and really belongs on a general AppleScript forum (macscripter.net or discussions.apple.com/community/mac_os/mac_os_x_technologies).But anyway, the problem is you misunderstand how `repeat with VAR in LIST` loops work. Variable VAR does not contain a list item, it contains a *reference* to a list item, e.g. `item 1 of LIST`. Depending on how you use that reference, it may get deferenced automatically and you’re none the wiser. However, when using it in equality operations (=, ≠) which do take value type into account, you must explicitly derefence it using `contents of REFERENCE`:if contents of index is equal to ind then …end ifdeveloper.apple.com/library/archive/documentation/AppleScript/Conceptual/AppleScriptLangGuide/reference/ASLR_control_statements.html
May ’20
Reply to AECreateAppleEvent from Swift issues.
NSAppleEventDescriptor is just a thin (and rather dumb) Objective-C class wrapper around an AEDesc struct, and includes methods for getting that struct data in and out. It does not support NSCoding so is not directly serializable, but I’m sure you can figure how to combine the aforementioned methods with AEM’s AEFlattenDesc/AEUnflattenDesc procedures yourself.And yes, stick to the Foundation classes as Quinn says. Pointer-heavy Swift/C interop is not much fun, and neither is the old Carbon AEM API† in general. It’s more polished and capable than the crude Foundation methods that wrap it, but it’s hella arcane and you’re just making work for yourself when you don’t have to.Oh, and as far as launching apps goes, you need to use LaunchServices for that (either what’s left of the old C LaunchServices API, or AppKit’s NSWorkspace). You’ll see all that in the SwiftAutomation code (which hauls in AppKit only because the stripped down LS* APIs no longer provide the means to locate/launch an app by file name).--† There’s a reason I totally redesigned the AEM API when doing an experimental reimplementation in pure Swift (github.com/hhas/AppleEvents). Much better API than either, though I quit work on it after realizing the Mach bridge uses a completely different serialization format to AEM itself (*after* I’d written it, derp) and wasn’t in a mood to reverse-engineer that too.
Apr ’20
Reply to Querying Class & Command Descs from NSScriptSuiteRegistry
I don’t think there is one. There’s a default Standard Suite definition at /System/Library/ScriptingDefinitions/CocoaStandard.sdef, which CS-based apps can XInclude in their own SDEFs (because nothing makes XML parsing more fun than having to deal with XIncludes too; this is why SA ended up using Cocoa’s bulky XML DOM API instead of faster, simpler CF SAX APIs), but AFAIK CocoaScripting’s default Text Suite is always included via cut-n-paste.Don’t expect too much rigor or polish from CocoaScripting. IIRC it was originally created by a NeXT engineer working blind from internal AppleScript documentation, and later patched up by the Mac Automation team into something that usually mostly works, but it has various legacy bugs and flaws in addition to the inevitable challenge of trying to be a robust, efficient, usable framework for a painfully underspecced AEOM. Fair attempt, but far from complete success.BTW, the standard Text Suite implementation is famously buggy, e.g. this will disappear your text before your eyes, so don’t run it on anything important:tell front document of application "TextEdit" move last word of paragraph 1 to end of paragraph 2end tell(It’s also, as I recall, fabulously slow; CS’s one-size-fits-all query-mapping abstraction doesn’t do well representing lots of single characters; quelle surprise. It would’ve made more sense to treat `character` elements as *character ranges*, i.e. substrings, which is what users invariably want anyway.)
Apr ’20
Reply to Querying Class & Command Descs from NSScriptSuiteRegistry
“Yes. This is exactly what Script Editor does when you run a script.”Not exactly. SE is just a host for OSA language components. The language itself is responsible for building and sending AEs. In AppleScript’s case, the parser uses lookup tables built from an app’s AETE resource to convert property/element/command/etc names in a script’s source code to the corresponding OSTypes (four-char codes) which are stored in the generated bytecode. On executing that bytecode, the interpreter calls the C AEM APIs to assemble typeObjectSpecifier and typeAppleEvent records and send them to the app process.Since OSA is built on the Carbon ComponentManager (which is an old and gnarly in-process plugin mechanism), and SE (also being old and gnarly) loads OSA components directly instead of into separate subprocesses as per modern recommendations, then yeah, outgoing AEs and their replies will be sent from and to the SE process itself, which is why Apple grant SE special exemption to standard sandboxing rules. But that’s a separate pain point/discussion, so let’s not get into that here.
Mar ’20
Reply to Querying Class & Command Descs from NSScriptSuiteRegistry
Be very wary of the SB documentation—it is a fiction, and very misleading. (There’s a reason SB and JXA never became popular, even though Mac Automation should be catnip for geeks.)As to CS documentation, it’s worth reading around. Third-party tutorials can offer insights that the Apple docs don’t. e.g. Here’s one by Matt Neuburg (old, but still worth reading as CS itself hasn’t changed much):www.apeth.net/matt/scriptability/scriptabilityTutorial.htmlOne good thing about Apple’s CS docs if memory serves: it’s the once place where they do talk about one-to-one and one-to-many relationships, which is the key to understanding how an Apple Event Object Model really works.Also bear in mind that CS’s behaviors (which are themselves not without bugs and quirks) only apply to apps built on CS, and don’t generalize to apps built on the older, lower-level Apple Event Manager APIs, which are much more variable in their behaviors...Yes, you can construct and send AEs yourself using NSAppleEventDescriptor. It’s low-level and a bit of a chore, but it can be done if that’s what you need. It’s what the “stable” version of SwiftAutomation on my BitBucket uses.†As for finding SwiftAutomation useful, nice to hear it. Alas, I couldn’t get Apple to take it (the offer stands), which is why I don’t see much point in pursuing it further. As far as studying other code, the only other AE bridge of worth is Frontier’s, but I don’t think you’ll glean much from that as it’s ancient, ancient C; besides I’ve already been through that and every other AE bridge myself and distilled all that knowledge into appscript, which in turn is refined into SA.You can message me via my SF profile (sourceforge.net/u/hhas), though as you can tell I’m pretty cheesed off at the whole mess and make no promises as to how helpful I can be.--(† There’s also an experimental fork of SA on my GitHub which assembles AE data directly, being intended as a modern cross-platform replacement to AEM itself, but ignore that. I got most of the way down to the Mach layer, but it’s a lot of effort to black-box reverse-engineer that last step when Apple themselves aren’t interested.)
Mar ’20
Reply to Querying Class & Command Descs from NSScriptSuiteRegistry
“That way, I'm guessing I'd make use of the AE codes in the sdef and not the <cocoa> element KVC-compliant key mappings, and use those with the Carbon framework?”I think you’re getting confused by the KVC data found in .sdef files. That data is not provided for clients’ use; it’s purely for use by the CocoaScripting framework upon which Cocoa-based apps implement their AE support. See `man 5 sdef` if you’ve not already done so.As to navigating an app’s object graph, run-time introspection is poor/non-existent. CS-based apps have a standard `properties` property, but this only tells you about attributes and one-to-one relationships; it doesn’t tell you what one-to-many relationships (elements) exist. Nor is it a standard feature in older non-CS based apps (e.g. most MS, Adobe apps). You have to resort to reading the app’s aete/sdef; however, even that is troublesome, e.g. SDEF does not distinguish between attributes of type `text` (really, typeUnicodeText) and one-to-one relationships of type `text` (really cText), so you’ve no idea if a given property is one or other except to try querying elements (e.g. `every character of PROPERTY`) and see if the event succeeds or fails. Human users, of course, learn an app’s object model and recognize which is which, e.g. `name of front document of app "TextEdit"` vs `text of front document of app "TextEdit"`; an automated crawler will not, so will have to trial-and-error extensively.And remember, AppleScript does not use any of that “type” information itself, so there are zero guarantees that information is accurate. e.g. Properties that can return, say, a string or `missing value` constant may declare their type as `text`, so even if you do a `get` there is no guarantee the result is a String. If you try to enforce that type (as SB does), you won’t get the right result. And that’s even before we consider implementation bugs in CS and/or apps, e.g. try `tell app "TextEdit" to get path of front document` and see what happens when the front document is newly created and hasn’t been saved yet. This is why SwiftAutomation leaves casting entirely to the client code, e.g. `PROPERTY.get() as String?`; it expects the code’s author knows that property returns either String or `missing value`, regardless of what the app’s dictionary may claim, so puts them in control. Works great, and takes advantage of the Apple Event Manager’s ability to coerce between various types so if you say `[String]` and the returned data is 1.0 then you’ll still get `["1.0"]`, but it’s not something you can automate as SDEF data’s not reliable enough.Another example: `tell application "Finder" to get entire contents of desktop`, where the dictionary defines the `entire contents` property as type `specifier` (typeObjectSpecifier). Run that command and it returns a list of specifiers. Yet you can also write `tell application "Finder" to get name of every file of entire contents of desktop`, so it’s clear the property’s type is not a list. As with `text of front document of app "TextEdit"`, or `text of every document of app "TextEdit"`, what you get it partly dependent on the query you ask, and partly dependent on what the app anticipates is the most helpful result for you. (Unfortunately, while AE IPC has the facility to do content negotiation—see keyAERequestedType—it is poorly specced, underpowered, and rarely supported by applications.)..Again, if you’re trying to extract all data from each running app, you are going to be crushed at how unspeakably slow it all is, even if you do apply each `get` operation to all elements at once. I just did a job for a customer, automating a task in Adobe Illustrator, which I prototyped in Python3+appscript because AppleScript is just a PITA. That script only sent a few thousand AEs; took half an hour to run!!! I dunno exactly what it was tickling wrong (a couple minutes is more typical performance), but even on a good day AEOM queries are hella expensive and any form of IPC is inevitably magnitudes slower than in-process messaging. (The production version, written for Illustrator’s own in-process JS engine: 10secs.)That’s why, in practice, we only query/manipulate the remote data we need. Pulling mass data over the wire is totally impractical performance-wise; plus there’s no transactional guarantees so even while you’re doing it the remote state may be simultaneously changing.Apple event IPC may have its virtues—e.g. I’ve long argued its query-driven model could be a great match for Siri’s query-builder (especially if the AEOM spec was tightened up)—but when you get down to details it is painfully rough to deal with.There’s two decades’ hard experience encoded in SwiftAutomation, and even then it’s not perfect, but it’s maybe worth a skim of the code just to get some appreciation of how and why it works the way it does. Because every other approach (gensuitemodule, JavaScriptOSA, aeve, RubyOSA, and of course ScriptingBridge) falls apart when real-world *AppleScriptable* apps stomp all over their assumptions.Oh, and for general insight into Apple event IPC and how and why it works the way it does (and why it was left so awkward and unpolished), read this paper by one of its original designers:www.cs.utexas.edu/~wcook/Drafts/2006/ashopl.pdfYou’ll learn more from that than you ever will from Apple’s own public developer docs, which are riddled with omissions and lies, it pains me to say.Not trying to deter you, mind (must be a pretty good idea to make you so determined to pursue it!); just letting you know what a world of hurt you are in for! 😉
Mar ’20
Reply to Querying Class & Command Descs from NSScriptSuiteRegistry
“ScriptingBridge tries to impose a Cocoa interpretation on an AppleScript world”This is why SB’s ORM approach doesn’t (and can’t) work right. ORMs covering RDBMSes are notoriously twitchy; and those only have to cover half a dozen apps speaking broadly the same language (SQL). In AppleScript’s world, there are hundreds of apps with wildly different structure and commands, and little consistency in behavior.The only information in an AETE/SDEF that AppleScript actually uses are the name/OSType mappings and the type of each (type/enum/property/command/parameter). Everything else is ignored—classes and inheritance are a fiction created for documentation purposes; even one-to-one and one-to-many relationships (containment) aren’t guaranteed to hold. (You’ll see this logic replicated in SwiftAutomation’s AETE/SDEF parsers, AS quirks and all.)It would really help if OP would describe the problem they’re trying to solve. We can talk about “how” till the cows come home, but unless we understand the “why” we don’t know if our advice is appropriate or not.
Mar ’20
Reply to Querying Class & Command Descs from NSScriptSuiteRegistry
“The idea is to get hold of the KVC definitions for the AS object graphs from a list of running applications and call them using KVC.”Apple event IPC = RPC + Queries, not OOP. It’s also notoriously high latency. Crawling the object graph will be murderously slow. Plus, application dictionaries are not IDLs; they are famously ambiguous/incomplete/incorrect, so there is a limit to how much you can rely on them.The more important question is: why do you want to do it? If you’re just trying to learn your way around scriptable applications, get Script Debugger which includes powerful dictionary and object graph browsers. If you’re thinking of writing your own, don’t bother: the whole AE/AS/OSA stack was put into maintenance mode and left to die quietly years ago. Barring miracles, its day is done.“I was hoping to simply use the Scripting Bridge to query SBApplications using the cocoa keys in the sdef.”There is no desktop automation task that cannot be made worse by trying to do it in Scripting Bridge. It’s junk. Stick to AppleScript. It’s the only [nominally] supported option that speaks Apple events right.If you like living dangerously then SwiftAutomation also speaks AEs right, but caveat emptor as I haven’t updated or tested it in a couple years and do not provide support.
Mar ’20
Reply to Persistant Privacy and Automation warnings
“AppleScript has been around since the early ’90s, and it’s shrugged off such rumours many times in the past.”The other thing that was around since the early ’90s was the Mac Automation/AppleScript development team. Even after its original creators quit in frustration at the lack of support, there was still a team to [nominally] work on it. However, the Mac Automation team got canned in 2016; and while I completely agree with Apple’s firing of its manager (and wish it’d had been done a decade sooner when a new manager could’ve been appointed to turn it around), it is apparent there are no longer any engineers tasked to AppleScript and its related technologies beyond a minimal maintenance role. (And even those minimal API additions made to accommodate securty are half-baked and poorly tested at best.)Beyond that, the AppleScript language itself is at the end of its lifecycle, and with no apparent moves to straighten out the very dated and arcane Apple event IPC systems that underpin it and bring that technology to iOS, it’s painfully obvious it has no future beyond slowly rotting away. All that tech all be fixed/replaced/reinvigorated, and reborn as the cross-platform cornerstone for all high-level interprocess communication and end-user automation, but there’s nobody left to do it; and even when there was they had no clue why or how! (Which is why there is nobody now.)Even Soghoian dropped broad hints post-ejection that Apple envisages the future of cross-platform automation in Siri-based technologies (now including [conversational] Shortcuts) and App Extensions, and he was the one who cratered its predecessor. And that is not necessarily a bad future, but it is still one far less capable or flexible that what could’ve, should’ve, would’ve had by now had the Apple events stack not been long since run into the ground.I know Apple seriously restricts individual employees discussing current and future plans, but I wish someone there would just be honest and officially declare the whole technology stack Legacy Technology to be Deprecated in a future release of macOS, so that all users now know exactly where they stand and can plan their own futures accordingly.†Yes, radical change hurts and they’ll complain like ****, but it’s still massively less damaging than radical change with zero notice. --† (And if you lose a few print shops to Windows as a result, so what? That hasn’t been a key market for Apple in 15 years, and is probably more trouble than it’s worth. At least with a timeline they can migrate sensibly, and customers generally appreciate a vendor—even an ex-one—who treats them professionally and doesn’t burn their business from under them.)
Dec ’19