Making it easier to keep my utilities up to date

The most common feature which you request in my free utilities is some form of built-in method of keeping them up to date. I’m delighted to announce that this is coming shortly – the first app in just half an hour’s time. I hope you can wait that long.

There are two widely-used methods of providing easy updates in a macOS app. One is to put the app into a service such as the App Store, which handles it for you, the other is based on the popular Sparkle mechanism to update apps in place. Although a few of my apps might survive review, I’m not enthused about putting lots of free apps in the App Store, I’m afraid.

Sparkle is very effective, and I happily update many apps here using it, but it relies heavily on developers providing secure update servers. Although it doesn’t exactly bypass macOS security protection such as ‘Gatekeeper’, it does remove the quarantine flag which is something which fills me with concern. It also has requirements which I would find either hard or expensive to meet from within this WordPress blog.

It was Matt @gingerbeardman who finally dragged me, screaming and kicking, to do something about this. The plan is that, when first opened, each of my apps will check to see if that version is the latest; if not, then they’ll download the current version using the default browser.

When you open an app, you normally do so because you’ve got something that you want to do. What you don’t want is for the app to take charge and start thrusting updates at you before you can get on with your work. In this scheme, the app fetches a Property List from my GitHub containing details of current versions of my apps. It finds itself in that list, and if the version listed there is different – in which case it must be more recent – it gets the download link from the list.

UpdateDecisions01

It then asks the user whether they want to download the update, or ignore it. If they opt for ignore, then the app lets them get on with whatever they wanted to do. If they opt to download the new version, the app calls their default browser to download exactly the same file that you’d get from any of the links to it that I post on here. The updater code does perform a check that the URL supplied hasn’t been hijacked.

You then end up with an automated download in the background, which places the Zip archive containing the new version, complete with its quarantine flag, in your default download folder. It’s left for you to unZip and install it when you’re ready to do so.

There are some important additional features. First, there’s a flag you can set in the app’s preferences which disables this behaviour completely, so that system administrators and users who want to install updates under their control can continue to do so.

Next, it’s possible that a user might open and close an app quite frequently during the day, and it’s pointless going through the full check every time. By default, the app only checks for updates every 12 hours, which should equate to each working day. If there’s a problem finding out if there’s an update available, an error in trying to do so, or you choose to ignore an update, you won’t be bothered again about it for another 12 hours. You can always use the Help menu command to check for updates if you change your mind.

You may also want to customise the interval between checks, so that’s another setting which you can change in the app’s preference file.

With a couple of minor exceptions, this process doesn’t report errors. If you’re working offline, for example, opening an app doesn’t keep telling you that it’s encountered error -2067 or whatever. An extra entry in the Help menu does though tell you whether the app has tried to check for updates. If you haven’t seen the dialog offering to download an update, you therefore know that either there isn’t an update available, or the app has been unable to obtain it.

The full flow chart for the app launch process, in terms of integrity and update checking, is now quite complex, as shown below. However it should mean that opening the app isn’t significantly delayed, nor do you keep tripping over annoying reminders about updates being available, when all you want to do is get on with your work.

UpdateDecisions02

In half an hour, the first of my apps to feature this process will be available: Revisionist. I’d be very grateful if you’d download that and run it at least once, so it can create its new preferences to support this process. In a couple of days I’ll release another (very minor) update to it, and you’ll then be able to see this mechanism at work, and report any problems.

It’s my intention to roll this out in new versions of my free apps as quickly as I can now. I hope that you find this a valuable improvement.

Tomorrow, I aim to explain in an article here how I’ve implemented this in Swift, for those who might like to adapt it for their own apps.

For reference, here’s the information about the preference items which control this:

  • noUpdateCheck, a Boolean. When set to true, this disables all update checking. Default is false.
  • updateCheckInt, a real number (Double). When set to a value greater than 1.0, the minimum time interval between checks, in seconds. Default is 43200, which is 12 hours. If you set it to any value less than 1, the app will reset it automatically to that default.

To change either of these, use a Terminal command of the form
defaults write co.eclecticlight.Revisionist updateCheckInt '10'
which works properly through the preferences server cfprefsd.