DEV Community

Prashant Nigam
Prashant Nigam

Posted on

How to add auto-update feature in macOS app: Step by Step guide to setup Sparkle framework (Part 2)

Recap of Part 1 - We learned what Sparkle is, and why we need it for apps released outside of the macOS App Store. We added Sparkle in our app via Cocoapods. We also learned few concepts related to Sparkle, how does Sparkle works, appcasts, how to serve appcast XML, and finally, we configured Sparkle to give the location info of our appcast XML, using which it can figure out whether a newer version of our app exists.

Let's begin this part where we left off in the previous. Build and run the app.

The below screenshot is the state of the app where we left off in the previous part.

Alt Text

Go ahead and try to update the app by clicking on the "Install Update" and you should see the following behavior with an error alert generated at the end, captured in video recording below:

Alt Text

Observations from using current version of the app (v1) thus far:
  • App remains open upon clicking the "Check for Updates" button. This behavior causes the Sparkle update alert hidden behind the App. This behavior is an issue, and we fix it in version 2 of the App. The app updates to version two via Sparkle
  • When we click on "Install Update," Sparkle downloads "a" version of "some app." We don't have a version two yet (heck, we don't even have the binary of current version one), so what does it download then?

Production ready

Releasing a macOS app, which can also be trusted by users, especially if we are distributing the App outside of the App store, means code signing and getting the App notarized by Apple. So let's do it.

For us to be able to test updates via Sparkle, our App should be in binary form and run from within the Applications folder (not via Xcode).

The first step in creating a binary of your macOS app is Archiving it. Archive your App in Xcode by clicking on the "Archive" option under the "Product" menu.

Alt Text

Once the generation of the Archive instance is finished, click Distribute App" Since we are distributing the App directly to our users, select the "Developer ID" option as the method of distribution.

Alt Text

Click "Next." You get an option to upload your App to Apple notary service or proceed without notarizing.

If you are thinking of proceeding without Notarizing your macOS app, then think again. While this tutorial is not about App code signing or Notarization, but I feel Apple notarization is very important and should always be considered, so adding more info about it.

Apple Notarization helps the user know that the App doesn't contain any malicious code

Notarization gives users more confidence that the Developer ID-signed software you distribute has been checked by Apple for malicious components.

Not only does apple notarization provide confidence for your user, but starting with macOS Catalina, an App distributed outside of App store must be notarized by Apple to run on macOS Catalina

Ok, now that we understood the importance of Apple notarization, let's move on to the next step, which is choosing the option to Upload to Notary service. Click "Next."

Alt Text

Boom, We see an error. Well, we did everything right, then what is causing this error? All comes back to code-signing and Notarization. The Apple notarization process is a topic in itself, and Apple covered it in great detail in WWDC 2019. You can watch the video here.

So let's get to resolving the issue that is causing the failure to upload to Notary service. First, describing the issue in brief. Not only our App code but any deeply nested code withing our App must also be code signed with our Developer ID, for us to be able to get our App Notarized by Apple. In our case, Sparkle code is embedded in our app binary, and also needs to be codesigned by our Developer ID. In the normal codesigning process, it does not automatically codesign embedded code/app, so we need to do it explicitly. In the following part, we add a step to codesign Sparkle and resolve upload to notary issue.

Codesigning and Notarizing Sparkle

In Xcode, head over to your project's Targets "Build Phase" tab. Click on + sign located on the top left and add a "New Run Script Phase." I call my run script Run Script - Deep Signing Sparkle framework

Alt Text

Inside script text field type in the following script:

#For Apple notarization, we need to deep sign Sparkle framework
LOCATION="${BUILT_PRODUCTS_DIR}"/"${FRAMEWORKS_FOLDER_PATH}"
IDENTITY=${EXPANDED_CODE_SIGN_IDENTITY_NAME}

codesign --verbose --force --deep -o runtime --sign "$IDENTITY" "$LOCATION/Sparkle.framework/Versions/A/Resources/AutoUpdate.app"
codesign --verbose --force -o runtime --sign "$IDENTITY" "$LOCATION/Sparkle.framework/Versions/A"
Enter fullscreen mode Exit fullscreen mode

This Run Script build phase should solve our failure to Upload to Notary service issue. Let's see if it did. Start over and Archive again. Once it is archived, click "Distribute App" and choose the "Developer ID" option. Click "Next" and finally the moment of truth. Choose "Upload" (send to Apple notary service) option and click "Next." This upload was the step that gave the error before.

This time upload to notary service works. See screen recording below.

Alt Text

Once Apple successfully notarizes our App, it sends us notifications. For a super simple App like ours, I got it within a couple of minutes.

Apple delivers notarization completion notification via both email and Xcode.

Alt Text

Alt Text

Alt Text

Finally, we are successful in notarizing our App. Now it's time to distribute our App to users. For that, let's export App in the form of the .app file (binary). The version of the Archive that got notarized has "Ready to distribute" under Status.

Alt Text

Click on the "Export App" button and save .app file.

Congrats, you have successfully finished creating a distributable signed and notarized first version of your App.

All that is left is moving the .app file to your Application folder. Go ahead and move it and launch your App by clicking on the App icon.

The App works similar to how it worked when we run it from Xcode. So now, we need to fix the issue we found earlier in this version of the App.

This brings us to the end of Part 2.

In Part 3, we fix this issue and update the App to version 2. Create version 2 binary and upload it to the cloud. Update our appcast XML, with relevant release notes, to notify to the (current) version 1 that a newer version is available for update. Finally, the current version of the App, sitting in our Application folder, gets updated to version 2 via Sparkle.

Well, we did quite some work, and so it's good to take a quick break. For some, a break could be taking a walk or stretch, then go ahead and do that (come back later for this last exciting part). However, if you one of those who find trivia to be exciting and save them for interesting party conversations, then here is one for you.

💡 There is just one letter that's not in ANY of U.S. state name. What is that letter? 🤔 Think about it and then head here to check if your answer was correct

Top comments (7)

Collapse
 
cool8jay profile image
cool8jay

A tip for someone that may meet codesign issue.

The script in project "SparkleSetupGuide" works fine on my Mac, but my own project gives a warning, saying "no identity found". After some research, I replace the line

IDENTITY=${EXPANDED_CODE_SIGN_IDENTITY_NAME}

with

IDENTITY="-"

and everything works.

Collapse
 
prashant profile image
Prashant Nigam

Thanks for the Tip

Collapse
 
d11r profile image
Dragos Strugar

Thanks a lot Prashant! It was helpful.

Collapse
 
prashant profile image
Prashant Nigam

Thanks for reading Dragos and and I glad you found it useful

Collapse
 
jonblum profile image
jon-blum • Edited

Hi Prashant -- do you know if this approach is still needed as of XCode 12.5 / Sparkle 1.26.0, or have things changed?

I've got a fairly complex app with helper tools which I'm trying to get notarized, and after the first couple of screens in the wizard (Distribute App > Developer ID > Upload) it brings up a "Select certificate and Developer ID profiles" dialog. I choose the profiles to sign my own components -- but there isn't one for org.sparkle-project.Sparkle.Autoupdate (and I can't create one, presumably because that ID is taken).

Any idea how to handle this?

Collapse
 
zeeshansuleman profile image
zeeshan

where is part third of this series?

Collapse
 
pradeepb28_ profile image
deepu

Hello Prashant, I followed your tutorial and now I am stuck for iterative updates of the app. Any plans to make the tutorial 3 as you mentioned?