Under The Microscope


Archive for September, 2019

The Finder Really Should Prevent Moving Running Applications

Yesterday, I wrote about how our software works to avoid issues if the application is moved while its in use. We shared some code for this which could be useful to other Mac developers, and Daniel Jalkut produced a more robust drop-in, which all Mac developers should check out.

Our products have handled this issue well enough for many years, and we were generally content with our solution. However, due to changes made in MacOS 10.15 (Catalina), we recently had to dumb down our approach. For years we used GCD (dispatch) to observe the application’s folder, as well as parent folders, to see if they moved. This doesn’t work very well on Catalina, where if the application is residing in the Downloads folder, the OS thinks the user needs to be warned we are “accessing” the Downloads area. To avoid frightening users with unnecessary security dialogs, our solution is now less robust than it once was.

After discussing all of this with Daniel, we came to the realization that this really isn’t something individual developers ought to need to handle at all. Instead, it makes the most sense for Apple to help users avoid this issue at the Finder level.

As much as one might want to move a running application, Apple’s Cocoa framework is simply is unable to correctly handle the situation at present. This leads to unexpected application behaviors and even crashes. To avoid these issues, the Finder already works to avoid multiple types of changes to running applications. For instance, if you try to delete an application that’s open, the Finder stops you:

As well, if you attempt to rename an open application, the Finder will warn against it:

This dialog allows you to proceed with the rename (and even makes that action the default), but it at least provides a warning that this action is not be advisable.

However, while any Unix geek can tell you that a rename is really just a move by another name, the Finder does nothing to stop you from actually moving the app. This seems like a real oversight, and something that can and should be fixed on Apple’s end.

I opted to file a bug with Apple about this (#FB7216674). Perhaps developers will one day see the Finder handling this for us.

Avoiding Crashes Caused by Application Moves

One of the best RSS readers on the Mac, NetNewsWire, has returned home to its original developer Brent Simmons. Late last month, Brent and his team of volunteers shipped version 5, and I’m delighted to see the return of NetNewsWire on the Mac.

Along with the release, Brent has been posting frequently to his blog at inessential.com. While discussing a piece he’d published on post-release follow-through, Brent noted “I keep remembering that I know things that I figure everyone knows — but then I remember that they don’t. So I write ’em up”.

This is a noble practice, and it’s inspired our own post, where we’ll be sharing some information and code.

Avoiding Crashes Due to Moved Applications

Following the release of NetNewsWire 5.0.0, Brent discovered the application had a crashing bug. Working with Daniel Jalkut of Red Sweater, they determined that the problem occurred when the application was moved in the Finder. When I saw Brent’s post detailing this issue, I knew we could help.

For many years, Rogue Amoeba’s applications have guarded against this very issue. If one of our applications is moved while it’s running, it will display an error like this:

This alert lets the user know they’re likely to run into issues, and urges them to quit and relaunch.1

Sample Code and Details

I discussed this issue with Daniel and Brent, and provided them with the code we’d been using to watch for this issue. This actually led us to make several changes and tweaks, and a simplified implementation of this “Application Moved” watcher can be found below:

- (void)_showBundleMovedAlertIfNeeded
{
    //This depends on bundleURL being cached and not updated when the app moves
    //Perhaps someday that will be false, but it's true now - 2019-09


    if( [[[NSBundle mainBundle] bundleURL] checkResourceIsReachableAndReturnError: nil] )
        return;

    NSString *appname = [[NSProcessInfo processInfo] processName];
    NSString *messageFmt = @"%@ has been moved or renamed";
    NSString *infoFmt = @"To prevent errors, %@ must be relaunched.\n\nIf you cannot quit immediately, click Continue, then quit and relaunch as soon as possible to avoid problems.";

    NSAlert *alert = [[NSAlert alloc] init];
    [alert setAlertStyle:NSAlertStyleCritical];
    [alert setMessageText:[NSString stringWithFormat:messageFmt, appname]];
    [alert setInformativeText:[NSString stringWithFormat:infoFmt, appname]];
    [alert addButtonWithTitle:@"Quit"];
    [alert addButtonWithTitle:@"Continue (Not Recommended)"];
    
    NSInteger alertButton = [alert runModal];
    [alert release];
    
    if (alertButton == NSAlertFirstButtonReturn)
        [NSApp terminate: self];
}

- (void)applicationDidBecomeActive:(NSNotification *)notification
{
    [self _showBundleMovedAlertIfNeeded];
}

This code was inspired by changes from Daniel, as well as Rich Siegel of Bare Bones. Whenever the application becomes active, we look if the bundle still exists where it should be, and throw an alert if not. This implementation is by no means perfect. For instance, if the application is moved and not activated for awhile, it could fail to notify the user before a problem hits. On the other hand, it gracefully handles other difficult edge cases, like the parent folder of the application being moved.

This is part of the shared code we use in all our applications, so improving it in one place will benefit all of our products. It’s good to revisit and refine things over time, and sharing this code with others has provided us with a nice opportunity to do so.

Closing

If you’re a Mac developer, you might have just realized why some previously inexplicable crashes have been occurring. With the above, you’re well on your way to handling the issue and avoiding crashes.

Over nearly 17 years of developing software for the Mac, we’ve created a lot of useful internal systems and generated a great deal of institutional knowledge. It’s good to be reminded that this accumulated knowledge can help other developers too. We’ll be keeping an eye out for more things we might share with other Mac developers.


Footnotes:

  1. It’s likely that most applications should simply force the user to quit, but as you can see, we also provide a “Continue” option. For our applications, the user may be in the middle of an important and uninterruptible task, like making a recording, so we don’t want to force them to quit the application. ↩︎

Our Software