Skip to content

When Open Does Not Return An Object Reference

There is a long standing bug in Cocoa Scripting, the technology Apple provides to developers to help make their applications scriptable, where the Open, Move and Copy commands do not return a reference to the newly created or moved object. This makes scripting these applications difficult.

I frequently get requests from Script Debugger customers for help solving this problem. I see people resorting to UI Scripting and many other techniques to address this problem.

The code below shows one way of working around the problem of the Open command not returning a reference to the newly opened document. I use the Preview application in my code, but this technique works equally well with other applications which fail to return a document reference from their Open commands. This technique fails is the file is already open.

set theFile to choose file — pick a file to open

tell application “Preview”

   set theOldOpenFiles to path of documents — sample the existing open documents

   open theFile — no new document reference returned – sigh

   set theOpenFiles to path of documents — sample the new list of open documents

end tell


—   Find the item in the list of new open documents that is not present in the old list of 

—   open documents.

set theNewFiles to finddifference of theOpenFiles against theOldOpenFiles


tell application “Preview”

   —  recover the reference to the newly opened document

   set theOpenedDocument to first document where path is (item 1 of theNewFiles)

   

   tell theOpenedDocument

      —  your code targetting the newly opened document goes here…

      set theDocumentName to name

   end tell

end tell


to finddifference of listA against listB

   —  This handler computes the ‘difference’ between the contents of two lists

   local newList

   

   set newList to {}

   repeat with a in listA

      set a to contents of a — dereference implicit loop reference

      if {a} is not in listB then set end of newList to a

   end repeat

   newList

end finddifference

8 Comments

  1. has has

    Pretty sure the bug is Cocoa Scripting: it’s flawed in concept, design, and implementation from the ground up.

    It’s such a shame Apple didn’t get AppleScript infrastructure veterans like you and Jon Pugh to create Cocoa Scripting for them – or even to rebuild it once it was obvious their first attempt sucked. It’s a hard challenge by any standard (relational set operations on ordered OO collections? mad!), but at least having devs already steeped in the technology – its capabilities, its problems, and how it actually works in the real world – would’ve given that framework a fighting chance to be genuinely great.

    The AS team may have patched up the most visible of the original unfortunate NeXT dev’s missteps by 10.4, but even if they had done serious work on it since (which they’ve not) CS will never be a good solution from either app developers’ or app scripters’ POV, not least because Apple in general (and the Automation team in particular) are so poor at eating that dogfood themselves. If they did, they’d know firsthand just how much it fails and might then be motivated to fix it, if only to make their own lives less painful.

    Good software is not born, it’s painfully extracted from the relentlessly flogged-to-death carcasses of previous attempts. The great Fred Brooks said to “write one to throw away” – and even that’s optimistic. Apple, for all its impeccable attention to hardware quality, frequently seems to forget this principle when delivering the software to go with it.

    Incidentally, I just tore a bunch off strips off Sal and his team this week in an email over what a miserable technical and popular failure their client-side APIs have been (SB, JXA, etc) – in large part because they never learn from prior works, talk to their users, or dogfood their own results. Reckon that’s the last time I’ll hear from that lot – most folks don’t like being told they’ve screwed up, even when it’s true – but I finally ran out of patience. Mac Automation is slowly starving for lack of new blood, even though desktop automation should be absolute catnip to every single geek on the planet. But what else can you call it when the Automation market is moribund while the rest of the Mac and mobile world is growing vigorously all around? PEBKAC for sure, and not on the user side neither.

    I’d like to hope Swift creates new opportunities to set all that legacy aside and start afresh with lessons learned, though it remains to be seen whether or not the fast-rising Swiftocracy believes scripting, automation, or end-user programming have any place in their Brave New World. Interesting times…

  2. Thanks for commenting Hamish.

    I agree, Cocoa Scripting is the problem. Problems with Cocoa Scripting are in large part responsible for the failure of my FaceSpan 5 effort. I had to spend so much time developing workarounds for Cocoa Scripting limitations that it starved me of time to develop FaceSpan its self.

    Frustratingly, I could not figure out how to opt-out of Cocoa Scripting and use my own C++ framework from Script Debugger 3 which is vastly better.

    I’ve always felt that Cocoa Scripting was a wonderful example of amazing Objective-C programming that failed to comprehend the AppleEvent inter-application communications model. Here are some of the most obvious failings:

    • open does not return an object reference.
    • move does not return an object reference.
    • copy does not return an object reference.
    • The Text Suite implementation does not allow you to provide a selection property
    • The get handler does not respect the requested type parameter
    • Cannot add properties to Text Suite objects
    • Mutations of text using the Text Suite implementation bypasses all of the Cocoa delegate callbacks making it impossible to respond.
    • Does not resolve property of property object references
    • Does not resolve property of record object references
    • Does not resolve item of list object references

    I could go on and on, but nobody within the Apple Automation team appears to care. If they did, someone would have shown some interest back when I gave up on FaceSpan. Unlike you, I don’t doubt their capabilities, but I do think they are focused on things we simply don’t see. However, I do agree that they fail to learn from their mistakes and have a lot of hubris.

    I hope the Automation folks don’t pull back from you after your email. I hope they realize that we get upset because we care and want to see these technologies succeed.

  3. has has

    “I’ve always felt that Cocoa Scripting was a wonderful example of Objective-C programming that failed to comprehend the AppleEvent inter-application communications model.”

    If I recall scuttlebutt correctly, the initial implementation was done by a lone ex-NeXT engineer with only the original [internal] AppleScript documentation to go by. It’s an attitude to product development that seems to be appallingly common in corporate software development, popular amongst both developers and managers: why should programmers waste their valuable time talking to users and learning their problem domain when they can just implement everying from spec?

    The fact that the resulting software is utterly unfit for purpose and utterly despised by its unlucky users is neither here nor there: it meets the spec, therefore its failure is not its authors’ fault.

    Eh… I suppose I shouldn’t complain TOO much: after all, it’s the only reason a low-functioning moron like me is able to stay in the game. I may not be a good technical coder, or even have much functional wetware left at all, but even so I can still sometimes run circles around much more qualified, experienced developers… all thanks to this one weird trick of simply talking to my users and learning what it is they do, how they do it, and why they do it the way they do, and accepting that when they say it’s not working right it means I’ve screwed up, not them (even if it’s just failing to explain it well enough).

    So while I’ve no doubt the AS team do satisfactory “behind the scenes” work (i.e. they’re competent maintenance engineers) I’ve also no doubt left that they’ve no real interest in their own users, or in producing work that actually meets those users’ needs. Or, at least, they care more about never admitting they’re wrong than about doing right by their users (and, ultimately, themselves too). What frustrates me is that when – as one of those (long-suffering) users – I go to them saying “Look, I can make your lives dead easy and your users incredibly happy at next to no cost to yourselves”, I just get smoke blown up my ass for a while then finally ignored completely. If an independent developer like yourself acted that way, you’d likely be shooting both your product and your business in the foot. But as Apple and others have taught me (the hard way, natch), at the corporate level, technical merit doesn’t mean squat; only who you know and how well you maintain the appearance of industry and efficacy, independent of any concrete results.

    ..

    There’s so much fundamentally wrong with CS (as you say, your own list is only scratching it) that it would be far quicker for someone like you (or me) to create a completely new framework from scratch. (And we’d get it right too, because we’d talk to our users, and treat the whole process as a two-way learning exercise, where the goal is not to grandstand or fluff one’s CV, but to produce something that users’ genuinely adore.) But that scale of undertaking only worth doing if you’re going to recoup the cost in some way – ideally though sale or sponsorship, though even just having it widely adopted would be sufficient.

    The problem is, as long as competing crapware occupies a uniquely privileged position in the OS, the first is a non-starter, and the second hardly any more likely. Which means you’re single-handedly trying to drive adoption of a better, independent product (a huge job in itself) over an inferior, official product amongst an audience of users who mostly don’t know enough to realize the difference and are thus inclined towards the official “safe” and “recommended” solution. All the while praying that the OS vendor on a whim doesn’t suddenly pull the rug from under yours. Been there, done that. Burned all of appscript’s users.

    I am not screwing up like that again, which means the only way I’ll put in that amount of work and persuade others to put their trust in the result, is if I can get it out my hands and into Apple’s so that it ships as part of the OS itself – thus avoiding both my unreliability and their rug-pulling in one fell swoop. I tried to start a dialog with Sal this month in hopes I could persuade him to steal SwiftAE from me (not unrealistic, since Apple already considered adopting appscript for 10.5), but got such an insipid, disinterested, form-letter-like response I finally let go with some harsh home truths.

    Funny enough, this rather brings us full circle, as in hindsight I regret never asking at the time why you were trying to use CS for FaceSpan. Even back then I knew it was not good or extensible software (despite being non-competent in ObjC/Cocoa myself), but an experienced and successful professional like yourself obviously knew far better than an idiot like me what you were doing. It never occurred to me that simply questioning your choice or voicing my silly and ignorant doubts might have proved a useful contribution in itself. At least Sal (once he’s doused the flames) can never say I made that mistake twice. 😉

  4. I fully understand your frustration. Back in the mists of time (Mac OS 8 I think), I tried to give Apple my JavaScript OSA implementation (I know you have misgivings about JSOSA but it showed what was possible). At the time, Jason Yao and Chris Espinosa were in charge of AppleScript and politely declined.

    As for FaceSpan, Matt Neuburg, you and I should have a long chat over a drink sometime. Matt really (I mean really) didn’t want me to use AppleScript at all. I felt that FaceSpan was an AppleScript product and the market expected FaceSpan 5 to continue to be an AppleScript product. Ironically, FaceSpan 5 was so vastly different from previous versions (And FaceSpan 4 had all but killed the product with its dependance on AppleScript Studio) that I could have gone with JavaScript/Lua/Ruby/Python/Whatever and had a product with much broader appeal. Anyway, I knew I could tame AppleScript but I was fearful of Cocoa Scripting and tried to find a way to opt-out and use my own framework. In the end I was forced into using Cocoa Scripting as I could not figure out a way of replacing it-its that deeply ingrained into Cocoa.

    The failure of FaceSpan really knocked the stuffing out of me. I wanted FaceSpan to work so badly. Up until that time I really felt I could hack my way out of anything but Cocoa Scripting proved me wrong.

    Script Debugger 4 was a reimplementation on top of Cocoa, and lost much of its scriptability and all of its attachability and recordability because of Cocoa Scripting’s lameness. All very frustrating.

    Anyway, I think we are in violent agreement on Cocoa Scripting.

  5. has has

    BTW, surprised you missed the biggest CS screwup: where applying move/duplicate/delete/etc operations to multiple elements results in user’s data dropping through the floor, never to be seen again. (Text Suite’s brilliant for this: just keep moving words about long enough and eventually your document’s empty!) But that’s what happens when one tries to apply relational database semantics to dumb ordered arrays, without thinking through the full implications: trying to reorder multiple objects arbitrarily and simultaneously makes for some seriously challenging math if off-by-N errors are not to occur. (In hindsight, lovely as it is when it works right, it would’ve been better had CS simply dropped this capability and concentrated on making single-object operations really, really fast.)

    The second-worst, IMO, is not making the whole interface inherently self-documenting, so that the user can query the implementation as to exactly what object specifier wants and forms a command accepts as parameters. Half the pain in automating apps is having to work out exactly which combinations of parameters will and won’t be accepted. All this detail is encoded in the implementation itself; what an oversight not to make it inspectable too. Instead, app developers have to manually add responds-to elements to their SDEF, which are a fat lot of help since most handlers dispatch on multiple parameters, not just their first.

    (Mind you, AS, CS, &co, have been a huge source of insipration in my own work – not just in how NOT to do things, but also in how things could be done if you just worked its ideas through to their logical conclusions. Here, this might warm your heart; current demos are underwhelming, but its predecessor was building seriously sophisticated artworks, including complex nutrition panels and even resizing and relaying elements. If you have some time to kill, skim the language docs and see if you can spot how many ideas I stole and/or bettered. Oh, and mad props to you for creating the powerful AE support that makes it all possible too!:)

  6. has has
    As for FaceSpan, Matt Neuburg, you and I should have a long chat over a drink sometime.

    Y’all ever have misfortune to land on Airstrip One, first round is definitely on me. 😉

  7. BTW, surprised you missed the biggest CS screwup: where applying move/duplicate/delete/etc operations to multiple elements results in user’s data dropping through the floor, never to be seen again.

    Oh, the pain-I think I must have blocked that out of my mind. I had to implement my own versions of move/duplicate/delete to deal with this very issue (and the returning of object references) for FaceSpan. Many hours wasted.

  8. Here, this might warm your heart

    I remember being shown your work on this a long time ago. It did warm my heart to see someone “get” what I was trying to enable and run with it.

Comments are closed.