Apple Gatekeeps
Provision profile, entitlements, signatures, notarization, gatekeeper, XPC, launchctl, spctl, sandboxes, launch agents, launch daemons. You have no idea what any of this is, and it’s time to release a new product to market. The previous developers, who are no longer with the company, promised it months ago. This is the situation I found myself in just a few months ago.
As unfortunate as this situation sounds, I still considered myself lucky. I was allowed to both rework an existing product to improve our codebase and create a new product. Additionally, I generally enjoy learning new things. However, I found that throughout my time learning the ins and outs of releasing an application on MacOS, I was often discouraged and depressed.
Introduction
I really wanted to try Golang for this new application. I had spent roughly a month or two working on the project solo, but now it was time to release it. Since I was leading the release and development of the application, I decided it would be best to offer a pre-release of the application to a select group of customers. The testing platform I had access to was limited.
My recommendation was to first offer a pre-release to a select few customers, second let the customers interact with the software for a few days to see if anything gets reported, third beta release to a broader audience, and finally continue with the full release of the application. Using this release strategy for new software makes the customers feel important and allows us to offload QA onto our userbase. I did feel a little scummy for offloading QA onto customers, but I didn’t really have much else of a choice.
Preparing For Release
Luckily, I already had a code-signing certificate set up from another application I had developed before. I remember having to jump through a series of hoops just to get that set up. Regardless, the general process I followed was as follows:
Codesign the application (the .app)
Create a DMG containing the application
Notarize the DMG
Codesigning
Now, you might be thinking, “Wow, who the fuck is this guy? That doesn’t seem like a terrible process.” However, if we just isolate number one, there are several points that have to be confirmed to ensure that this step is done correctly.
Directory structure is correct in your
.app
Valid and correct
Info.plist
fileProperly configured certificate installed in your keychain
Again, this is a relatively short list to check; however, in my experience, the documentation online for even just the directory structure is not well-defined. I had to rely on external sources (not Apple) to ensure I was following standards. And if I was going to rely on Apple, I had to rely on a toddler-aged forum post from “Eskimo.”
This post is not going to act as a guide on how to set up each of these tedious components to walk through a successful release on macOS. Instead, this post is going to serve as a venting mechanism for me to whine. Conversely, if there is enough whining, I will produce a walkthrough of what I did to release.
Creating a DMG
Creating a DMG is relatively easy; however, I find that dealing with them is quite cumbersome. Why not use a standard compression method? I don’t really know. Regardless, I didn’t struggle much with this component of the release. A simple Google search will reveal the command you need to convert your .app
to a .dmg
.
Notarizing the DMG
I’m still not completely sure what this step actually entails. However, in layman’s terms, this is the process of submitting the DMG to Apple and hoping that you pass whatever checks run on the other side. If you happen to fail a check, you should pray that the error message you find actually makes sense. For example, let’s say you forgot to include an Info.plist
file in your .app
directory. Once you reach this step, you’d use the program suite under xcrun
to submit the DMG. After waiting about 1-2 minutes, you’d see “rejected” along with a log GUID. Useful, right? Now, you need to take your GUID and run a separate command to output the logs of your submission. Then, inspect the resulting log file. You’ll find a lot of, in my experience, useless metadata about the submission. Somewhere in the file, you’ll find an error message explaining, hopefully, what actually went wrong. I found that most of the time these error messages were cryptic. The only way I was able to tell what actually went wrong was by consulting the magic conch that is “Eskimo.”
Wrapping Up
Now, you think you’re almost done, right? We’ve jumped through the hoops, and we’ve learned that the only real source of knowledge about any of Apple’s distribution issues is a single employee who lives in an igloo. After all that, you want to make sure that everything is working as intended. Mount your DMG and start up your application, success! It actually works. Send over your resulting DMG to the QA guy, and he says the app is telling him he can’t run the app on that version of macOS. I look it up; supposedly, the LSMinimumSystemVersion, when not specified, is somewhat variable depending on the project. After adding the flag to the Info.plist
, I found that the application was still not supporting my request for the OS version. I finally caved and decided to use another solution to package my application. Before, I was doing everything by hand, on the command line. I refactored my build code to use fyne-cross
. It added a few additional flags to my Info.plist
. There was no indication why any of the flags that were added would force the OS to respect my LSMinimumSystemVersion
request.
Nevertheless, I was over it at this point and decided to just let the package do the work for me. Going through this process killed any desire I had to learn the inner workings of packaging a macOS bundle myself. In conclusion, don’t be fooled; macOS not only wants to keep users addicted to their ecosystem, but it also wants to ensure developers have to go through hell to actually release a product on their platform.
Subscribe to my newsletter
Read articles from 0x45 directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by