Chapters

Hide chapters

macOS by Tutorials

First Edition · macOS 12 · Swift 5.5 · Xcode 13

Section I: Your First App: On This Day

Section 1: 6 chapters
Show chapters Hide chapters

16. Distributing Externally
Written by Sarah Reichelt

In the last chapter, you looked at the advantages and disadvantages of distributing your app through the Mac App Store. Then, you worked through the process of testing, distributing and updating using the Apple system.

Not all apps are eligible for the Mac App Store, and there are other reasons why you might want to distribute externally. In this chapter, you’ll look at what you need to do to distribute your app outside the App Store.

If you only want to use the Mac App Store, you can skip this chapter. Come back to it if you change your mind.

Apple’s Gatekeeper

macOS has a system called Gatekeeper to help protect our Macs from malware.

Open System Preferences ▸ Security & Privacy. In the General tab, you’ll see Allow apps downloaded from:. This is where you configure Gatekeeper:

Configuring Gatekeeper
Configuring Gatekeeper

There used to be an option to allow apps from anywhere, but Apple has locked that down. Now, you only have two options: App Store or App Store and identified developers.

To include your app in the identified developers category, it must be code signed and notarized. You upload the app archive to Apple for their servers to notarize it.

In the previous chapter, you created an Apple Developer account and linked it to Xcode. If you skipped that section, go back and do it now, so that you can be one of these identified developers. :]

You’ll need an Xcode project to work with. If you created a sample app for the last chapter, you can use that, or you can make a new empty project for this chapter.

Open your project in Xcode and make sure you’ve selected your Team in the project’s target settings under Signing & Capabilities.

While you’re there, make sure that Hardened Runtime is enabled. This does even more work to lock down your app and protect your users from malicious code. Apple will not notarize an app without this.

If you can’t see a Hardened Runtime section in Signing & Capabilities, click + Capability and double-click Hardened Runtime in the palette to add it.

Enabling hardened runtime
Enabling hardened runtime

By default, Xcode leaves all the options unchecked, but if your app uses any of the listed features, you’ll have to check them.

Change the Build in the target settings General tab. If you’re using numbers, you can increment this. Otherwise, update it using whatever scheme you prefer.

It’s a good idea to get into the habit of changing the build before starting any new distribution step. Even if the app is unchanged, it’s best to have different builds for App Store distribution and external distribution because they get processed differently.

Now, use Product ▸ Archive to create a new archive in the Organizer window.

Exporting the App

In the Organizer ▸ Archives window, click Distribute App to see the four possibilities:

Distribution options
Distribution options

You used the App Store Connect option in the last chapter. Now, consider the others.

Copy App creates a folder containing the built app. Because you’ve selected your developer team, it’s already code signed.

You can run it on your own computer, but if you give it to someone else, they’ll see this:

Can't run app
Can't run app

The key phrase here is that Apple cannot check it for malicious software. In the next section, you’ll get Apple to perform this check.

People can overrule Gatekeeper and run the app by right-clicking it and selecting Open from the popup menu. They’ll get another warning dialog and then the app runs. Interestingly, macOS quarantines it to a temporary AppTranslocation folder.

I asked ReallyUsefulApp to show where it was running from and got this, even though I’d run the app from the Applications folder:

App Translocation
App Translocation

I think that’s the first really useful thing the app has ever done. :]

Development is similar to Copy App, but if you’re part of a developer team, you can send this version to other members of the team who’ll be able to run it in the same way you can.

These are useful methods for testing, but no good for wider distribution.

Notarizing the App

The only option you haven’t looked at yet is Developer ID. That’s the one which allows you to send your app off to the Apple notary service so they can confirm it’s clean. This isn’t the same as app review. Apple only checks that your app does not contain any harmful code.

Select Developer ID and click Next.

Now, there are a few more questions for you to step through, clicking Next after each one:

  • Select Upload, which is the option that sends the app off to the notary service.
  • Confirm your development team, if you’re asked.
  • Go with Automatically manage signing.

If you don’t have the developer certificates installed yet, Xcode offers to generate them for you, but if you worked through the previous chapter, they’re already in place.

Finally, you’ll get to the summary, where you click Upload:

upload summary
upload summary

It may take a few minutes, but you’ll eventually see a dialog saying your app has been successfully notarized. There’s an Export… button on this dialog, but click Close instead and go back to the Organizer window.

Note: The Upload dialog might say your app has been uploaded, and you’ll receive a notification when it’s ready for distribution. Simply close this window and wait for more information to appear in the Organizer window.

Apple’s notary service has checked your app, confirmed it isn’t doing anything malicious and stored this information on their servers.

In the Organizer window, you’ll see more information at the bottom of the right sidebar. You may need to expand the window to see it all:

Organizer window
Organizer window

Note: If your app failed the notarization process, click Show Status Log to see what went wrong.

The Identifier is the unique ID assigned by the notary service.

Note: There might be a short delay between your app’s status changing to Ready to distribute and the appearance of the Identifier and Export Notarized App button.

Now, click Export Notarized App to export a fully code-signed and notarized version of your app. Congratulations!

Wrapping Your App

You’ve exported the app and it looks like a file, but actually, it’s a folder. See what’s inside by right-clicking the app and selecting Show Package Contents:

App package contents
App package contents

This means your app is not in a good state for emailing, or for upload and download. Before releasing it, you need to wrap it up somehow.

There are two main ways to do this. The simplest is to create a zip file. Right-click the app and select Compress “<app name>” from the contextual menu:

Compressing
Compressing

This creates a single file you can email or make available for download. Apple distributes Xcode betas like this, although they use XIP files, which are digitally signed zip files.

The main disadvantage of this method is that the app is likely to end up in the user’s Downloads folder and not in their Applications folder where it should be.

The better alternative is to create a disk image. This takes a bit more work, but it encourages users to install your app in their Applications folder.

Creating a Disk Image

Setting up a disk image, or DMG, is a three-step process:

  1. Create a new blank disk image and add your files.
  2. Configure the window display.
  3. Create a locked copy of the disk image for release.

To start, open Disk Utility from Applications ▸ Utilities. Go to the File menu and choose New Image ▸ Blank Image…. Fill in the file name and image name. Make sure the size is larger than your app and leave all the other default values:

Creating a disk image
Creating a disk image

Click Save and Disk Utility makes your image. Open it to mount the image, and you’ll see an empty Finder window. Now, you can put files and folders into it.

You’ll add two items — your app and an alias to the Applications folder. This way, when the user opens the image, they can drag the app directly into the correct location without having to open another Finder window.

To add your app, drag it from wherever you exported it, into the disk image Finder window. You’ll see the green plus sign as you drag, showing that you’re making a copy.

The Applications window is a bit trickier. You don’t want to copy your Applications folder, you want to add an alias that points to the Applications folder on the user’s system.

Open your Applications folder in a new Finder window. Right-click the word Applications in the title bar or toolbar to pop down a menu showing the folder’s parents. Select the next one down, which is probably Macintosh HD:

Applications
Applications

With that open, select Applications and Command-Option-drag it into your disk image window. You’ll see a curved black arrow on the icon as you drag, indicating that you’re creating an alias.

Configuring the Display

Now, your image has two visible items, but the next thing you’ll add will be invisible. Disk images frequently have a background image with instructions or logos. You can use whatever image you like, but if you don’t have one, open the assets folder in the downloaded materials for this chapter and locate background.png.

Drag background.png into your disk image window and then press Shift-Command-. to show invisible files.

Next, rename the image by adding a period at the front of the name, so it’s now .background.png. Finder may warn you that this will make the file invisible, but that’s what you want.

Note: Finder doesn’t allow you to rename a file with a leading period unless you’re already showing invisible files.

The next task is to configure the window so it opens looking the way you want your customers to see it.

In your disk image window, press Command-1 to View ▸ as Icons, then press Command-J to open the View Options. Set them as follows:

  • Check Always open in icon view: This also checks Browse in icon view.
  • Select None for Group By and Sort By.
  • Set the icon size to 80 x 80 or whatever suits your background image.
  • Drag the grid spacing to its maximum setting.

View options
View options

Next, you’ll set the background image. Select Picture in View Options ▸ Background and drag your background image into the image well:

Setting the background picture.
Setting the background picture.

With that in place, close the View Options window, then press Shift-Command-. to hide the invisible files.

To make your disk image window look less cluttered, go to Finder ▸ View and select — toggle to Show — all the Hide options you see: Hide Sidebar, Hide Preview, Hide Toolbar, Hide Tab Bar, Hide Path Bar and Hide Status Bar. You may have hidden some of these views already, but hide all the others:

Hiding Finder options
Hiding Finder options

When all these menu items start with Show instead of Hide, you’ve done it.

Now, move your two icons into the appropriate locations on your background image and resize the window so that your background image fills it:

Disk image window
Disk image window

Locking it Down

The final step is to lock your disk image by creating a read-only copy of it.

In another Finder window, eject your new disk image by selecting it and pressing Command-E. This unmounts it, but you still have the DMG file.

Open Disk Utility again and go to Images ▸ Convert…. Select your DMG file, give it a different name and set the image format to read-only:

Converting the disk image.
Converting the disk image.

Click Convert and wait while Disk Utility creates your new image. This also resizes it to suit your app. Open your new image, and you’ll see it appear exactly as you set it.

Note: If your new disk image doesn’t show the background picture, eject and delete the newly created read-only image. Open the writable image again and make sure it’s set up correctly. Move or resize the window to force Finder to write its configuration to the .DS_Store file, and then try again.

Further Notarizing

Some sources suggest you should also notarize the disk image or zip file. I have not found this to be necessary, but if you want to do it, there are detailed instructions in Chapter 14 of Catalyst by Tutorials.

Selling Your App

You’ve prepared the app for distribution. Apple’s notary service has approved it and you’ve packaged it ready for downloads. Now what?

How are you going to deliver your app to your customers? Are you going to charge for it? You’ll need a web site for hosting your app and information about it. And if you’re charging for the app, you’ll need a payment system.

If the app is free, host it on your web site and start telling people about it. There are lots of web site builders that can get you started quickly, if you don’t already have a site. Squarespace is one possibility, and it also offers payment facilities if you’re charging for your app.

Another option is to pass the responsibility on to a third-party reseller. In this case, Paddle is a good option to consider. They charge a fee but take a lot of the pressure off you.

Releasing Updates

When distributing your app externally, you need to solve the problem of updates. In the App Store, updates get pushed out to users automatically, but when you’re distributing yourself, you have to handle this.

The simplest option is to have a file on your web site with the latest version information. Your app can check this periodically and, if it’s newer, direct the users to the download site to get the update.

Your app needs a way to get its version and build. This method does that:

func versionAndBuild() -> String {
  if
    let bundleInfo = Bundle.main.infoDictionary,
    let version = bundleInfo["CFBundleShortVersionString"] as? String,
    let build = bundleInfo["CFBundleVersion"] as? String {
    return "Version: \(version) (\(build))"
  }
  return "Version: unknown"
}

This extracts two entries from Info.plist, checks they’re strings and assembles them into a single string.

A more automatic way is to use a framework like Sparkle. This is a very popular open source library for adding update facilities to Mac apps.

Whenever you update, you have to create a new Xcode archive and get the new version notarized before release.

Troubleshooting

Sadly, you don’t get crash reports for apps from outside the App Store. People can still make reports, but these disappear into the Apple servers, never to be seen again.

Because of this, it’s important to give your users a way to contact you directly to tell you about any problems. You can do that by including a button or menu item that calls this method:

func emailDeveloper() {
  // 1
  let subject = "Really Useful App \(versionAndBuild())"
    // 2
    .addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!

  // 3
  let link = "mailto:dev@example.com?subject=\(subject)"

  // 4
  if let url = URL(string: link) {
    NSWorkspace.shared.open(url)
  }
}

What does this do?

  1. Use the previous method to get the version information and appends it to the app name.
  2. Encode this to work as a query in a URL. You know what’s going to be here, so force-unwrapping is safe in this case.
  3. Create a mailto link containing the recipient and the encoded subject.
  4. Convert the link into a URL and use NSWorkspace to open it in the default email app.

Adding a link to your social media accounts is also a good idea, especially for people who only use web-based email clients. Use NSWorkspace.shared.open() to open these in the browser.

Key Points

  • While distributing outside the App Store frees you from some constraints, you still need to get Apple to notarize your apps.
  • An app is actually a folder, so you need to wrap it for distribution.
  • Handling payments for the app is now your responsibility, although there are resellers that can help.
  • You’ll have to implement a process for getting updates out to your app’s users.
© 2026 Kodeco Inc.