Category Archives: Automation

Toggle System Grayscale Mode

A colleague recently asked whether it was possible to connect a custom keyboard shortcut to the system-wide “Use grayscale” setting in the macOS Voiceover system preferences:

Screenshot of macOS preference options for accessibility options inlcuding 'Use grayscale'

I could not find any easy way to do this, and searching the web for solutions revealed that most people are addressing this want by using GUI scripting to automate literally opening System Preferences and clicking the pertinent checkbox.

I thought there must be a way to do this in a more streamlined fashion. Couldn’t the option be automated via AppleScript or something? After some brief research, my conclusion was “no.”

At this point I put on my “hacker hat” and proceeded to analyze the System Preferences code that handles the configuration. It’s a binary in /System/Library/PreferencePanes, and the following Terminal command got me on the right path:

cd /System/Library/PreferencePanes/UniversalAccessPref.prefPane/Contents/MacOS/
nm UniversalAccessPref | grep gray

In short, that means “dump all the symbols (nm) from the VoiceOver preference pane, and search them (grep) for the word ‘gray'”. Here’s what it spits out:

0000000000057210 S _OBJC_IVAR_$_UAPDisplayViewController._grayscaleCheckbox
                 U _UAGrayscaleIsEnabled
                 U _UAGrayscaleKey
                 U _UAGrayscaleSetEnabled

These look to me like exactly the names of functions that the preference pane is calling in order to check the current state, and to set the updated state, of the “Use grayscale” checkbox. The capital “U” stands for “Unimplemented.” I.e. it expects to find these symbols, function names in this case, in another library. But which library?

otool -L UniversalAccessPref

The “otool -L” command will dump all the libraries that the preference pane “links to,” meaning the libraries it expects to load functions or data from. There’s a huge list of frameworks in the output, but the most interesting one to me is:

/System/Library/PrivateFrameworks/UniversalAccess.framework/Versions/A/UniversalAccess

The framework name “UniversalAccess” correlates strongly with the “UA” prefix on the pertinent function names we dug up above. Great, so how do we call these? They’re private system functions which means you should not rely on them for production code, but for a quick hack to make toggling grayscale easier? It’s a reasonable risk in my opinion. Here’s a simple C program that takes advantage of the private methods to simply toggle grayscale mode on or off, depending on the current setting.

If you wanted to assign this functionality to a keystroke, as originally suggested, the easiest way in my opinion is to use an app like my own FastScripts. You could drop the compiled binary above into your ~/Library/Scripts folder, and run it directly from FastScripts. Or, if you don’t want to fuss around with compiling a C program, just copy and paste this AppleScript:

-- Line up a Python script for dynamically loading 
-- the private framework  and invoking the required
-- private methods to get current grayscale mode
-- and set it to the opposite value.
set toggleGrayScript to "python -c 'from ctypes import cdll
lib = cdll.LoadLibrary(\"/System/Library/PrivateFrameworks/UniversalAccess.framework/UniversalAccess\")
lib.UAGrayscaleSetEnabled(lib.UAGrayscaleIsEnabled() == 0)
'"
do shell script toggleGrayScript

This script takes advantage of Python’s ability to dynamically load an arbitrary shared library and invoke its exported functions. I wondered if I might be able to use AppleScript’s own “use framework” functionality but I couldn’t quite figure it out.

Hopefully this has been instructive generally for folks who are interested in hacking at system frameworks, and specifically for folks who were looking for an AppleScript for quickly toggling macOS grayscale mode on and off.

Finder Quick Actions

In the What’s New in Cocoa for macOS session at WWDC 2018, Apple announced Quick Actions, which are handy little contextual tasks you can invoke on selected items in the Finder, either from a contextual menu or from the Preview side panel.

The emphasis in the session was on creating Quick Actions via Automator. There, it’s as simple as creating a new workflow document, selecting “Quick Action” from the template palette, and saving. It even puts it in the right place (~/Library/Services).

Essentially, Quick Actions appear to be macOS Services, which have a long history and which Automator has previously been able to create. In fact in macOS Mojave betas, the Quick Action document seems to completely supersede the “Service” type.

But what about native applications that want to provide Quick Actions? I didn’t see anything in the WWDC session to address this scenario, so I started poking around myself. When you click the “More…” button in Finder’s Preview panel, it opens up System Preferences’s Extensions settings, focused on a special “Finder” section. In the list are several built-in extensions.

I thought these were likely to be implemented as binary app extensions, so I instinctively control-clicked on one. A “Reveal in Finder” optionĀ appeared, so I selected it. Sure enough, they live inside Finder itself, and are packaged as “.appex” bundles, the same format that Apple supports for 3rd-party applications.

What’s handy about finding an example of an app extension you want to emulate, is you can open up its bundle and examine the Info.plist. Apple’s approach to identifying app extensions’s capabilities and appearance is based heavily on the specification of values in an NSExtension entry. Looking at one of Apples models, I saw confirmation that at least this variant was of type “com.apple.services” and that its attributes included many useful values. NSExtensionActivationRule, are substantially documented, and can be used to finely tune which types of target items an extension can perform useful actions on.

Others, such as NSExtensionServiceAllowsFinderPreviewItem and NSExtensionServiceFinderPreviewIconName do not appear to be publicly documented yet, but one can guess at what their meaning is. I’m not sure yet if the icon name has to be something public or if you can bundle a custom icon and reference it from the extension. I was alerted on Twitter to at least one other key: NSExtensionServiceAllowsTouchBarItem, which evidently triggers the action’s appearance in the Touch Bar while a qualified item is selected.

Dumping AppKit’s framework binary and grepping for likely matches reveals the following key values which are pretty easy to guess the meaning of:

NSExtensionServiceAllowsFinderPreviewItem
NSExtensionServiceFinderPreviewLabel
NSExtensionServiceFinderPreviewIconName
NSExtensionServiceAllowsTouchBarItem
NSExtensionServiceTouchBarLabel
NSExtensionServiceTouchBarIconName
NSExtensionServiceTouchBarBezelColorName
NSExtensionServiceToolbarPaletteLabel
NSExtensionServiceToolbarIconName
NSExtensionServiceToolbarIconFile
NSExtensionServiceAllowsToolbarItem

Of course, until these are documented, and even when they are, until macOS Mojave 10.14 ships, you should consider these all to be preliminary values which could disappear depending on further development by Apple of the upcoming OS.

JavaScript OSA Handler Invocation

When Apple added support to macOS to support JavaScript for Automation, they did so in a way that more or less allows folks who invoke AppleScripts to invoke JavaScript for Automation scripts as if they were exactly the same. An abstraction in Apple’s Open Script Architecture (OSA) makes it easy for script-running tools to theoretically handle any number of scripting languages without concern for the implementation details of those languages.

This mostly works, but I recently received a bug report that shed light on a problem with Apple’s implementation of JavaScript with respect to invoking a specific named handler. The OSA provides a mechanism for loading and running a specific handler, or function, within a script. My app FastScripts takes advantage of this to query a script about whether it would prefer to be invoked in another process or not. Unfortunately, when it comes to JavaScript, Apple’s implementation runs the whole script in addition to running just the specific, named handler.

If you’ve got Xcode handy, you can use this simple playground content to observe the problem:

import OSAKit

if let javaScriptLanguage = OSALanguage(forName: "JavaScript") {
   let scriptSource = "Application('Safari').activate();" +
         "function boo() { ObjC.import('Cocoa'); $.NSBeep(); }"
   let myScript = OSAScript(source: scriptSource, language: javaScriptLanguage)

  // Only the behavior of boo should be observed
  myScript.executeHandler(withName: "boo", arguments: [], error: nil)
}

// Give time for the beep to sound
RunLoop.current.run(until: Date(timeIntervalSinceNow:5))

The named function “boo()” only invokes NSBeep, so when this playground is run, all that should happen is a beep should be emitted from the Mac. Instead, when it runs Safari becomes the active application. This is because in addition to running the “boo()” handler, it also runs the whole script at the top level.

A workaround to the bug is to wrap the top level functionality of a script in a “run()” handler, so where the scriptSource is declared above, instead use:

   let scriptSource = "function run() { Application('Safari').activate(); }" +
         "function boo() { ObjC.import('Cocoa'); $.NSBeep(); }"

I hope this helps the one other person on earth who cares about invoking JavaScript for Automation methods indvidually! (Radar #33962901, though I’m not holding my breath on this one!)

Re-signing Code

Given the implied urgency of Apple’s demand that developers provide updated versions of apps with “version 2” code signatures, many people are scrambling to get their build processes updated so that e.g. the apps are built on 10.9 or higher. This is the most natural and attractive technique for ensuring that your Mac apps are signed properly.

However, for a variety of reasons many of us either need to build with older versions of Mac OS X or Xcode. We face a conundrum that can be solved by signing (or more accurately, re-signing) the apps on 10.9, ensuring that the signature is up to snuff even if the code was compiled and the app was assembled with earlier tools.

This is a fairly straight-forward process, but there are some gotchas and you should be aware of what effect running codesign with various flags will have on your finished product. In a nutshell, you want to establish new code signatures on all the binaries in your bundle, but you don’t necessarily want to reset specific entitlements or other metadata that was set on the code by your old-and-busted code signatures.

Felix Schwarz offers a bash script suited to re-signing and further packaging the resulting app into a .pkg file suitable for submission to the Mac App Store. If you’re looking at automating this process, his script may serve as a good starting point!

In my tests I ran into some issues having to do with the fact that some of my apps have custom “designated requirements.” I have ironed out all the kinks yet but it seems to help in this scenario to re-establish all the code signing for the bundle first and then as a final icing on the cake, re-sign the app package with the custom designated requirement.

JavaScript Bug Traps

I am the kind of developer who leans heavily on automated systems for the improvement of my code. For this reason I tend to enable the maximum number of warnings, rely upon static analysis to discover nuanced bugs, develop and run unit tests to automatically confirm expected behaviors, and I encourage my apps to crash hard if they are going to crash at all.

I would characterize all of these behaviors as part of an overall attitude of vigilance against software defects. Learning about problems in code quickly and clearly is a key step maintaining an overall high level of code quality.

Most of my software is developed in Objective-C, which enables me to leverage compile-time and run-time tools from Apple that catch my errors quickly. Between compile-time warnings and errors, and runtime exceptions and crashes, the vast majority of my programming errors in Objective-C are caught within minutes of writing them.

On the other hand, MarsEdit features a rich HTML text editor largely implemented in JavaScript, a language which tends not to afford any of these convenient alerts to my mistakes. I have often stared in bewilderment at the editor window’s perplexing behavior, only to discover after many lost minutes or hours, that some subtle JavaScript error has been preventing the expected behavior of my code. I’ll switch to the WebKit inspector for the affected WebView, and discover the console is littered with red: errors being dutifully logged by the JavaScript runtime, but ignored by everybody.

Wouldn’t it be great if the JavaScript code crashed as hard as the Objective-C code does?

In fact, these days mine does. By a combination of JavaScript hooks that are alerted to the errors, Objective-C bridges that propagate them to the host app, and the judicious use of undocumented WebKit Inspector methods, development versions of my WebView-based apps now automatically present an inspector window the moment any error occurs in the view’s underlying JavaScript code.

For those of you who also develop JavaScript-heavy WebKit apps, I’ll share the steps for enabling this same type of behavior in your app. These steps all apply to Mac-based WebViews, but most of the same techniques could be used with iOS if you are willing to use some private methods to establish a bridge between the WebView and host app (for debug builds only, of course).

Step 1: Catch the error in JavaScript.

In whatever the main source content is for your WebView-based user interface, add some script code to register a callback for JavaScript errors:

<script type="text/javascript">
   function gotJavaScriptError(errorEvent)
   {
      console.log("Error: " + errorEvent.message);
   }

   window.addEventListener("error", gotJavaScriptError,  false);
</script>

Now if you encounter some error and happen to be attached with the web inspector, you’ll see additional confirmation that the error has in fact been noticed. if you’re following along in your own code while you read, I strongly encourage you to add a testing mechanism for generating errors. A simple “click” event callback (or “touchstart” on iOS) does the trick well:

<script type="text/javascript">
   function gotClick(theEvent)
   {
      blargh
   }

   window.addEventListener("click", gotClick, false);
</script>

Now when you click, you blargh. And when you blargh, the error is logged. Of course, just logging the error isn’t very useful because it’s quiet and goes ignored. Just like JavaScript errors usually do…

Step 2: Propagate the error to the host app.

You could do some interesting stuff in the WebView itself. For example you might convert the error into a string and display a prominent alert message. But since there’s so much more you can do once the host app is in charge, it’s worth going the extra mile for that functionality.

To tie the host app to the WebView we need to wait for the WebView’s frame to finish preparing its “window script object,” and then set a named attribute on that object that refers to an Objective-C instance. See that your WebView has a frameLoadDelegate configured, and within that delegate object implement this method:

- (void)webView:(WebView *)webView 
        didClearWindowObject:(WebScriptObject *)windowObject
        forFrame:(WebFrame *)frame
{
   // Expose ourselves as a JavaScript window attribute
   [[webView windowScriptObject] setValue:self forKey:@"appDelegate"];
}

At this point any JavaScript code, including our error-handling callback, can reference the Objective-C delegate directly. But in order to make good use of the delegate we’ll have to add a couple other methods. The first lets the WebKit runtime know that we are not fussy about which of our methods are accessible to the WebView:

+ (BOOL) isSelectorExcludedFromWebScript:(SEL)aSelector
{
   return NO;
}

Now JavaScript code within the WebView can call any method it chooses on the delegate object. Implement something suitable for catching the news of the JavaScript error:

- (void) javaScriptErrorOccurred:(WebScriptObject*)errorEvent
{
   NSLog(@"WARNING JavaScript error: %@", errorEvent);
}

And back in your WebView HTML source file, amend the JavaScript error handler to call through to Objective-C instead of pointlessly logging to the JavaScript console:

<script type="text/javascript">
   function gotJavaScriptError(errorEvent)
   {
      console.log("Error: " + errorEvent.message);
      window.appDelegate.javaScriptErrorOccurred_(errorEvent);
   }

   ...
</script>

Notice that the colons in Objective-C method names are simply replaced with underscores to make them compatible with the JavaScript function naming rules. The errorEvent argument in this case is translated to a DOMEvent object instance in Objective-C, where it can be further interrogated. Alternatively you could pull the parts of the event you want out in JavaScript, and pass along only those bits.

Now we’ve done a considerable amount of work, only to upgrade our easy to ignore JavaScript errors from littering the web console to littering the system console. At least we’d notice these errors in the Xcode debugger console, and might eventually get around to taking a closer look. But we can still do much better.

Step 3: Get face to face with the error.

Of course from Objective-C there are any manner of ways you could handle the error. In a shipping app it might make sense to prompt the user with news of the issue and offer, much like a crash reporter would, to send information about the error to you. Or if the contents and behavior of your WebView are critical enough, maybe it’s worth forcing the app to quit just as it would for a native code crash. But for debugging builds I’ve find it very helpful simply to have the error force the Web Inspector open so I am no longer able to quietly ignore all the red console logging. In the delegate class, go back to the javaScriptErrorOccurred: method and replace the NSLog call with this string of fancy mumbo-jumbo

- (void) javaScriptErrorOccurred:(id)errorEvent
{
#if DEBUG
   id inspector = [[self webView] performSelector:@selector(inspector)];
   [inspector performSelector:@selector(showConsole:) withObject:nil];
#endif
}

That’s it. Now when you run into a WebView-based JavaScript error, the web inspector appears and the list of pertinent errors is front-and-center.

I encourage you to leave the DEBUG barrier in place, because as the prolific use of “performSelector” may suggest, these are private WebKit methods that would probably not be viewed as acceptable by e.g. App Store reviewers. Anyway, you probably don’t want customers being pushed into the Web Inspector.

I hope this technique proves useful for those of you with extensive JavaScript source bases of your own. For everybody else, perhaps I’ve helped to drive home the idea that we should be as vigilant as possible against software defects. All developers write bugs, all the time. It’s only fair that we try to balance the scales and give ourselves a fighting chance by also being alerted to the bugs we write, all the time.

F-Script Anywhere With LLDB

Ever since the start of my career at Apple, working with the venerable Macsbug, I have prided myself on making the most of whatever debugging facilities are at my disposal. I came to be quite capable with Macsbug, adding custom commands and data templates that helped me speed through crucial debugging sessions that would have otherwise taken much longer.

When I moved from the classic Mac OS team to Mac OS X, I was forced, only slightly earlier than every other Mac developer, to adapt to gdb. I did so with modest aplomb, adding custom commands that made it easy to, for example, continue until the next branch instruction (as it happens, also the first real post on the Red Sweater Blog).

When Apple started shifting away from gdb to lldb a few years ago, I realized I would have to throw out all my old tricks and start building new ones. To my great shame, progress on this front has been slower than I wished. The shame is made greater by the fact that lldb is so delightfully extensible, it practically begs for a nerd like me to go town adding every manner of finessing to suit my needs.

The nut of lldb’s extensibility is that much of its functionality is actually implemented in Python, and developers such as ourselves are invited to extend that functionality by providing Python modules of our own.

I finally decided to break the ice with lldb’s extensibility by adding a shortcut command for something I often want to do, but frequently put off because it’s too cumbersome: injecting F-Script into an arbitrary application running on my Mac. F-Script is a novel dynamic programming interface that lets you query the runtime of a Cocoa app using a custom language. It also features a handy tool for drilling down into an app’s view hierarchy and then navigating the various superviews and subviews, along with all their attributes. In some respects it’s very similar to a “web inspector,” only for native Objective-C applications on the Mac (and sadly, with far fewer features).

There are Automator workflows that aim to automate the process of injecting F-Script into a target app, by running the required commands, via gdb or lldb, to make the injection work seamlessly. For some reason, these workflows have never worked so seamlessly for me, so I’m always reduced to attaching to the process with lldb, and running the required commands manually to get the framework loaded.

Fortunately for me, “having to run lldb” is not such a big deal. Usually when I want to poke around at an app, I’m in lldb anyway, trying to break on a specific function or method, or examining the application’s windows and views via the command line. Once I’m attached to a process with lldb, getting F-Script to inject itself is as easy as running these two commands:

expr (void) [[NSBundle bundleWithPath:@"/Library/Frameworks/FScript.framework"] load]
expr (void) [FScriptMenuItem insertInMainMenu]

That’s all well and good but to do that I always have to find the memo I took with the specific commands, then copy and paste them individually into lldb. Far too often, I wind up imagining the struggle of this work and put it off until I’ve spent minutes if not hours doing things “the harder way” until I finally relent and load F-Script.

Today I decided that I need to stop manually copying and pasting these commands, and I need to finally learn the slightest bit about lldb’s Python-based script commands. The fruit of this effort is summarized in this GitHub gist. Follow the directions on that link, and you’ll be no further away than typing “fsa” in lldb from having all of its utility instantly injected into the attached app from lldb.

Even if you’re not interested in F-Script Anywhere, the gist is a concise example of what it takes to install a simple, Python-backed macro command into lldb. Enjoy!

AppleScript XML-RPC

My mind was fairly well blown this morning to learn that for more than ten years, AppleScript on Mac OS X has included a built-in command for communicating with XML-RPC and SOAP endpoints on the web.

XML-RPC is a well-known semi-standard method for communicating between processes over the internet. As it happens, a large number of blogging APIs including the WordPress API and many others, were also designed around XML-RPC. For this reason, XML-RPC is a major component of MarsEdit, my Mac-based blogging application.

The funny thing is, I knew that Apple had developed built-in support for SOAP: one of my teammates at Apple was responsible for it! But I either blocked out the XML-RPC support, or disregarded it as uninteresting at the time. And I don’t think I ever knew that the support had been extended to AppleScript in such a native fashion.

XML-RPC isn’t, as they say, rocket science. However, it’s pretty cool that with an off-the-shelf Mac one can throw together a simple script to, for example, grab the latest post off your blog, build a link to it by its title, and copy the HTML to the pasteboard:

set myBlogUsername to "sweatertest"
set myBlogPass to "xxx"

tell application "http://sweatertest.wordpress.com/xmlrpc.php"
	set myPosts to call xmlrpc {method name:"wp.getPosts", parameters:{"1", myBlogUsername, myBlogPass, "1"}}

	set myPost to item 1 of myPosts

	set theLink to link of myPost
	set theTitle to post_title of myPost
	set myPostLink to "<a href='" & theLink & "'>" & theTitle & "</a>"
end tell

set the clipboard to myPostLink

Wire it up with a FastScripts keyboard shortcut and you’re really cooking! This example may be a bit contrived, but one can imagine writing similar scripts to query a blog for information, or even to fire off short blog posts after prompting for content. (Note that if you do automate something like this you will probably want to store the password securely in the keychain).

People love to hate AppleScript, but this is one example of how many nifty little treats lurk within it. The fact that it’s omnipresent on Mac OS X makes it an excellent resource for providing simple solutions, when a simple solution will do.