When AIR Applications Prevent Shutdown or Restart on Mac OS X

Update (7/27/2011): I just added another partial solution at the end of this post.

My friend Ben Simon occasionally uses AIR when he has clients who need cross-platform desktop applications. Recently, Ben discovered that an AIR application he’s working on is preventing OS X from shutting down or restarting. Specifically, when the application is running, choosing "Restart…" or "Shut Down…" from the Apple menu results is the OS claiming that the AIR application canceled the restart.

I started looking into the issue for him, and the first thing I discovered is that this definitely doesn’t happen with all AIR applications. I was able to narrow the issue down pretty quickly to just applications that stay running even though all their windows are closed. Further investigation led me to discover that the issue has to do with calling event.preventDefault() on the closing event thrown by NativeWindow (one technique for having applications "minimize" to the system tray on Windows or the Dock on Mac is stop the main application window from closing and hiding it, instead).

From what I can tell, there are two pretty solid ways to work around this issue:

  1. Use custom chrome. If you draw your own window chrome, rather than listening for the CLOSING event and calling event.preventDefault(), you can just listen for the click event on the close button in the window chrome and set the visible property on the window to false right there.
  2. If you really want or need to use system chrome, the best work-around I’ve come up with so far is to move your main application into a separate component that extends NativeWindow (let’s call it Main.mxml). That way, you can set the visible property of your main application window to false, then just open and close new instances of Main.mxml as you need to (as opposed to toggling the visible property).

There are probably other ways to get this to work, but unfortunately I’ve run out of time to test them right now. If you have your own techniques, feel free to post them in the comments.

Update (7/27/2011): A partial solution occurred to me this morning. If you check to see if your window is visible before calling preventDefault(), your application won’t stop the OS shutdown in cases where all your application windows are already closed. The following code will do the trick:

private function onWindowClosing(e:Event):void
{
    if (this.visible)
    {
        e.preventDefault();
        this.visible = false;
    }
}

The reason this is only a partial solution is that it only stops preventDefault() from being called when the window isn’t visible, but if your application has visible windows, preventDefault() will still be called which will still cancel the OS shutdown or restart. Better than nothing, but still not perfect.