Distributing Mac apps outside the App Store, a quick start guide

Jan 8 2021 10:00 AM

The Mac has always been very different from its close relative, iOS, especially when it comes to what a user is or is not allowed to run on their system. Even with the introduction of Apple Silicon, Apple has made it very clear that the Mac is still the Mac, and is still hackable, even when running on the new architecture.

What this means for us developers is that, when targeting the Mac platform, we have choices: we can distribute our apps independently, outside the Mac App Store, through the Mac App Store exclusively, or through both at the same time.

This article is my brain dump on the subject. It is meant to be a guide on the things that you’ll need to know about when distributing a Mac app outside the App Store, rather than a how-to tutorial. My hope is that having everything listed here will help demystify the process for beginners, and the descriptions of my own process will be useful as starting points.

App Store x Direct: pros and cons

All of these choices come with their pros and cons, and depending on which type of Mac app you’re making, you might not be able to have it in the Mac App Store to begin with. An example of that is my app AirBuddy which, in order to provide deep integration with Apple’s wireless devices, needs to run a system agent and use some private APIs, which would never be allowed in the App Store. The same goes for many other types of apps which simply wouldn’t work with the restrictions of the Mac’s sandbox.

For those who do have that choice, I’ve compiled a list of what I believe to be the pros and cons between shipping through the Mac App Store or shipping directly.

Mac App Store pros

Mac App Store cons

Direct distribution pros

Direct distribution cons

A note on Catalyst and SwiftUI

With the introduction of Catalyst, we’re now seeing many new Mac apps being released, since it’s a lot easier to take an existing iPad app and turn it into a Mac app. Apps ported to macOS through Catalyst are not required to be released in the App Store, even if their counterpart on iOS is.

Additionally, there is currently no TestFlight for macOS (one of my wishes for 2021), so if you’d like to distribute beta builds of a Catalyst app, you’ll have to do that outside the Mac App Store, and it is not that different from distributing a production app.

A lot of what I’m presenting here will also apply for Catalyst apps — they’re Mac apps, after all — but some might require additional hacking in order to work around the fact that Apple doesn’t want you to use the entirety of AppKit directly from within a Catalyst app. With a bit of work though, you can make a Catalyst app very Mac-capable, including support for AppleScript and other features.

For SwiftUI apps targeting the Mac, there should be no major differences with the distribution process, since you can use all features of the macOS API in a SwiftUI app without requiring a lot of hacking like it does for Catalyst apps.

Distribution

Distribution of an app involves two parts: actually uploading, storing and serving the app binary and its updates somewhere, and also producing the right package that will work for your users.

Hosting

The first major step with getting your Mac app in the hands of users without the App Store is to figure out how to distribute its binary. No App Store means that you’ll have to host your app’s binaries and updates somewhere on the internet and provide a link for your users to download it.

There are several ways you can go about this. For an open-source app, you can use Github releases and even host your app’s update feed in the Github repo. That’s how I distribute the WWDC app for macOS.

For my commercial apps, I’ve been using Backblaze B2 for storage of both the app binaries, delta updates and update feed, and proxying all requests through Cloudflare so that I can have a custom domain for the downloads/updates and also add filtering, caching and logic on the server if needed.

B2 is an extremely affordable provider (I rarely pay over US$1 in a month). Most Mac apps are not that large in size, so even if your app is downloaded a lot, it’s unlikely that you’ll end up having to pay a lot of money for storage/bandwidth. Another popular option is using Amazon S3 buckets, but their control panel gives me nightmares so I prefer to use B2 which is a lot simpler (and less expensive).

I haven’t automated the publishing step for my app releases as of yet, so to upload a new release I just use Transmit as a client for my B2 buckets. Speaking of that, before we even get to upload a release to whatever provider we’ve chosen, there’s a very important step: getting the right file to put out there.

Notarization and packaging

When exporting an archived app from within Xcode, we get two main options for distribution: App Store Connect and Developer ID. To distribute apps without the App Store, you’re going to be using Developer ID.

The same developer account you use for distributing apps to the Mac App Store can be used to sign your apps for Developer ID distribution. The certificate itself is different, but Xcode will auto-generate and install one for you during the process of exporting the archive if you haven’t done so yet.

Since macOS Catalina, all apps distributed directly to users must be notarized by Apple, otherwise they won’t launch by default. This process uploads your app to Apple, which will then run automated malware checks and “staple” your binary with a special signature that will allow it to run. This is not App Review, it’s an automated check to prevent malware from being distributed through this method, and it is also a way for Apple to flag a single binary for malware, instead of a developer’s entire account, should it become compromised at some point.

Whether or not you notarize the binary directly from within the Xcode organizer will depend on which packaging method you’ll be using to distribute your app. We can’t just upload a .app directory to a server and let users download that, we have to turn it into a flat file. The simple way to do that is to just zip the app and distribute it as a zip file, but I’ve found through experience that distributing the app as a DMG file reduces support requests by quite a bit.

You’ve probably seen DMGs before when downloading Mac apps. They’re disk images that are mounted by macOS when double-clicked in Finder, and they can also provide some artwork instructing the user to drag the app into their Applications folder. This makes it easy for a user to figure out what to do, and it also reduces the chances that a given user will be running your app from their Downloads folder or some other random place like that.

If you’re going to be distributing your app as a DMG, you should just export it using the Developer ID option in Xcode, without notarization, then notarize the DMG itself. There’s no option in Xcode to export a DMG, so you’ll have to use a third-party tool. The one I like to use is create-dmg. I’ve also created and open-sourced dmgdist, a tool that automates the process of creating, uploading and stapling the DMG so that you can get the image ready to be distributed by running a single command.

To distribute the app as a zip file, the process is simpler: pick the upload option from Xcode after selecting “Developer ID” and it’ll produce a notarized version of your app, which you can then zip up and distribute directly.

App updates

Another aspect of the App Store is that it also handles app updates. Whenever we upload a new version to App Store Connect and it gets approved, users receive the update in the App Store. For apps distributed directly, we need to replicate that somehow.

The best way to do that — and the most common — is to use Sparkle. It’s been around for many many years and is pretty much the official way to distribute updates for Mac apps distributed outside the Mac App Store.

Sparkle is currently living a double life of sorts. You can either use the “legacy” version of Sparkle or use a more modern “v2” branch which includes many improvements such as the ability to update sandboxed apps. I still use the “legacy” version because it’s the one that I’m familiar with and I find that integrating the more modern version is still a bit more complicated. If it ain’t broke, don’t fix it.

The process of generating an app update usually goes as follows: ensure that with every update you increase the app’s version (of course), produce the package as described before (Sparkle can handle zips, DMGs and installer packages), then use the generate_appcast tool to update the feed. After doing that, upload the deltas, the package for the new version, and the updated AppCast feed to your hosting method of choice, at which point users will start seeing the new version when they check for updates within the app.

It may sound complicated, and it definitely has a learning curve to it, but after you get things set up and working, it’s a really smooth process (way better than dealing with App Store Connect, if you ask me).

Making money outside the Mac App Store

If you’re looking to distribute your Mac app outside the App Store, chances are that at some point you’d like to make some money from it. Just as with the App Store, there are lots of different business models you could adopt, but by far the most common for apps distributed directly to customers is the good old paid upfront model: the user pays to download the app, registers it using a license key and gets updates for free, at least for a certain period of time.

Another business model that’s common for apps distributed outside the App Store is the subscription model, where users pay a certain amount monthly or yearly to keep using the app. Picking a business model could be an entire guide (or series of guides) by itself, so I’m not going to help you with that. I’ll assume paid upfront — which is the model that I use for my apps — for the remainder of this section.

In order to get paid for your product, you’ll need a storefront that users can visit to learn about it and (hopefully) purchase it. A good option for beginners is Gumroad, which offers a storefront page, payment processing, hosting, and licensing. When I first released AirBuddy back in January 2019, I used Gumroad, and it served me very well, with tens of thousands of copies sold during that year.

However, Gumroad was not initially designed to sell software, so it lacks some flexibility that other providers give you. With the release of my new app FusionCast and also AirBuddy 2.0, I moved over to Paddle, which is now handling payment and licensing for my apps.

Another option is to simply use a payment provider, something like Stripe or FastSpring, and handle all of the fulfillment and licensing yourself. That way you get ultimate flexibility, although it is more work and will likely require you to hire additional providers (to send emails, for example).

I’d say that if you’re looking to make some money on the side by selling a Mac app outside the Mac App Store, Gumroad is the best option for you, since they handle pretty much everything and you won’t even have to create a website for your app. However, if you’re selling apps as a company or as your main source of income, a more professional solution such as Paddle will have fewer limitations and offer more flexibility.

Licensing, copy protection and piracy

A concern you might have about distributing Mac apps directly is piracy: anyone can get your app’s binary and run it without necessarily paying for a license, unless you employ some sort of copy protection.

While that is true, I’ve found that it is not worth it to developers — especially indies — to spend any significant amount of time working on copy protection. Yes, a few people out there are going to steal your work, but those are people who wouldn’t have paid for your app anyway, and any time you spend worrying about it or coding in some super advanced DRM into your app is time away from fixing bugs and developing new features. Additionally, this type of practice tends to end up punishing legitimate users more often than it stops piracy (just look at the numerous examples from the game industry).

The first version of AirBuddy didn’t even include any sort of copy protection, not even a simple registration form for the user to enter their license key. I did find a few pirated copies of the app available online (some of them with malware included, of course), but I saw no evidence that a large percentage of users were pirating the app, and my numbers didn’t reflect that either. In version 2, I’m using the Paddle SDK for registration during the app’s onboarding, but that’s all I’m doing.

Apps distributed through the Mac App Store are not automatically protected from piracy either: you have to manually check the App Store receipt to make sure the copy is legit. Most receipt verification code is trivial to crack, so an app distributed through the Mac App Store is no more protected from piracy than an app that’s distributed directly.

Marketing

I’m including this section mostly to make a point: there’s no major difference in marketing between distributing your Mac app directly or distributing it through the Mac App Store. These days, simply releasing an app in the App Store means almost nothing, since it’s very unlikely that users will just organically discover a brand new app without any external input.

What you might be able to skip when distributing through the App Store is having a website for your app, since the App Store page can then be used as the main storefront for it, but even then I’d say most apps can benefit from having a dedicated landing page that’s not just the App Store product page.

Marketing apps could be yet another guide on its own, but in general you’ll want to use every channel that’s available for you, especially if you already have a following (Twitter, Instagram, TikTok, etc). Sending your app (including a free license) to websites and people who cover Mac apps can also be a great way to get it out there. You can also do paid advertising on social media, podcasts, and publications.

That’s it!

I hope you found this guide useful. If you have any questions or comments, feel free to reach out on Twitter.