DEV Community

Prashant Nigam
Prashant Nigam

Posted on • Edited on

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

In this multi-part tutorial, I'll walk you step by step on how to add and configure the auto-update framework Sparkle in your macOS app. Sparkle has an excellent basic setup page but in my opinion, it is a bit too advanced and maybe a bit overwhelming to follow along. In this tutorial, my effort will be to make it easy for someone to add and configure Sparkle.

If you are familiar with the macOS app releases and why Sparkle is needed, then you can skip the next section and start here.

Why Sparkle?

To answer this, let's rewind a bit. So you had an idea about a macOS app. You researched and found no perfect app (solution) exists that solves the problem and you decide to solve by building the perfect app. You burn the midnight oil and finally come up with a build of an app, which is in a state that you are not too embarrassed to showcase to the world. You ship your first version.

Post shipping is when work truly begins. User downloads your app and provides their feedback. A cycle begins which starts by listening to the user's feedback, incorporating them and shipping the second version. And third and fourth and it continues. In this cycle, a very critical element and the question is how to provide new versions to users?

One way to do it is via Apple's Mac app store. While it's great option (no hosting fees, no monthly storage and bandwidth fees especially if your app is free and app is auto-updated with new versions) but it comes with mandatory requirements, for e.g. Sandbox should be enabled etc., that one needs to implement in their app and which may not allow your app to work in a way that you think provide the best experience. So the only option left is hosting it outside of mac app store.

How do you then provide new versions of your app to users seamlessly?

This is where Sparkle comes in

"Sparkle is an easy-to-use software update framework for macOS applications."

One should always have an auto-update feature in their app starting with the first release, lest you will be in my shoes πŸ˜”.

Personal plug: I released a mac menu bar app EasyFinder and hosted it outside of mac app store.

To provide peace of mind to users and of course assurance to macOS Gatekeeper ☺️ so that it let EasyFinder run, EasyFinder has been dev signed and notarized by apple. It doesn't have an auto-update framework and so I cannot push new versions to users and alert them. The second version of EasyFinder will be a new launch on Product Hunt, something one may not want unless it's a mega update. Thankfully the second version of EasyFinder will bring a whole bunch of new experiences and features which warrant a hard launch instead of a soft launch.

However, lacking the ability to push new version limits me in a way. I would have liked to push a few minor updates to get user's feedback on some new features that I am building before doing a version 2 launch. Thus in my experience, I would strongly advise adding auto-update feature in your macOS app starting with the first release. This is what this tutorial is about. Adding Sparkle to an app before it's first released or in some cases like me post first release.

Getting Started

Let's get started. I will take you step by step on how to setup Sparkle framework in your macOS app and last but not least, configure it properly so that apple agrees to notarize it 😎. Yes, Sparkle needs to be code signed as well or else your app will not be accepted for notarization. Why should we bother about Apple notarization? More on this later.

We will add and configure Sparkle to an existing macOS project. I have created a starter project. Go ahead and download the project. After downloading, build and run the project. If everything goes well you should see a Star icon in your menu bar πŸ‘‡
Menu bar icon

Click on the star and it will open a pop up displaying a label "First Version"

First version of the app

Clicking on the button labeled "Check for Updates" doesn't do anything. It's just a placeholder for now and shortly we will add functionality to this button to check if there is a newer version of the app available

Adding Sparkle framework to Xcode project

We will use cocoapods to add Sparkle. So first let's add pod file by opening Xcode project location in Terminal and typing

pod init

Directory content before initiating pod
Directory content before initiating pod

Directory content after initiating pod
Directory content after initiating pod

As you can see in the screenshot above, a new file named Podfile has been added to the project directory. In this new file, we will add the Sparkle framework pod name. Open Podfile in your favorite editor and add following in the file

pod 'Sparkle'

Alt Text

Now that we have declared, via Pod, that our project requires Sparkle framework, it's time to install the pod. Close the Xcode project.

Go back to the same location in Terminal and install the Sparkle framework by executing the following command:

pod install

Alt Text

This will add Sparkle framework as a dependency in your Xcode project and will also create a new Xcode project file with extension xcworkspace.
Alt Text

From now on we will use this file SparkleSetupGuide.xcworkspace to open our project. Open your project using this file. Your Xcode project should have a Pod project like in the screenshot below
Alt Text

Build and run your project. The result should be similar to when we built and ran before installing the pod.

Configuring Sparkle

Open Main.storyboard file

Open library window by either clicking "+" above right side pane or by using short cut Command + Shift + L. Search for 'Object' and drag it to Application Scene

Alt Text

Update the class for this newly added Object to SUUpdater in Identity Inspector

SUUpdater - This class is used to configure the update parameters as well as manually and automatically schedule and control checks for updates

Sparkle gives the user an option via an alert, whether they prefer to automatically check for updates or they want to do it manually. This happens usually after the first launch of the app. Since we have added Sparkle updater object, we should see an alert on or after the second run. Run the app, shut it and then re-run it. You should see below alert:

Alt Text

Let's recap what we have done so far. We have added the Sparkle framework in our project via cocoapods. Subsequently, we added an Object in our storyboard's Application Scene and updated that Object's class to SUUpdater. The object will now be able to manually or automatically check for updates

Now, since the user can choose to check for updates manually, we should provide that feature in our app. Remember that dumb button "Check for updates" that did nothing? Well, let's make button intelligent and useful so that the user can use it to check if there is a new version of the app available.

Note: Since checking for updates is not the core functionality of an app, you will usually provide this in the preference section of your app instead of putting it in the main screen as we did

Open VersionDisplayViewController.swift file

Add the Sparkle module by importing it. Add following import

import Sparkle
Enter fullscreen mode Exit fullscreen mode

Now update checkForUpdates function with following code:

    @IBAction func checkForUpdates(_ sender: Any) {
        let updater = SUUpdater.shared()
        updater?.feedURL = URL(string: "some mystery location")
        updater?.checkForUpdates(self)
    }
Enter fullscreen mode Exit fullscreen mode

The above code is getting an instance of class SUUpdater, setting some mystery location to instance's feedURL property and then asking SUUpdater instance to check at that some mystery location if there is any new version of the app available.

Clicking on the button does nothing but it will generate following error in Xcode console:

[General] You must specify the URL of the appcast as the SUFeedURL key in either the Info.plist or the user defaults!
Enter fullscreen mode Exit fullscreen mode

No one likes seeing errors and nor do we. We will get rid of them shortly but there are a couple of new jargon in this error. appcast, SUFeedURL key. Further, you might be wondering about this some mystery location and why are we hard coding (not a best practice)? and how does Sparkle figures out if and when there is a new version of your app is available?

First, let's understand how Sparkle works

Sparkle uses appcasts to get information about app updates. An appcast is an RSS feed with some extra information for Sparkle’s purposes.

In plain words, Sparkle polls "some location" on the internet, a location that we provide to Sparkle either via code (like above) or in app's Info.plist, and determines whether the app version is up to date or if there is a newer version available. If there is a more recent version available, then Sparkle informs the user via an alert. In a moment we will see what this alert looks like.

Now that we know how Sparkle works, let's talk about that some mystery location. It is the location of our appcast file for our app and it has info on app's versions. Sparkle expects an XML file in that some mystery location. Using info in the XML file, Sparkle determines whether the user has the latest version or not.

You can download a sample appcast XML file from Sparkle and go through it.

It is strongly recommended that appcast XML is served via HTTPS URL. I will use Amazon S3 to host the appcast XML file for the purpose of this tutorial. You can choose from a plethora of options available online for e.g. Firebase Storage, your companies own server as long as it is HTTPS, Azure etc.

Following screenshot is from S3 location:
Alt Text
URL of my appcast XML file is:

https://s3.amazonaws.com/com.sparklesetupguide.tutorial/sparkletestcast.xml
Enter fullscreen mode Exit fullscreen mode

This is the URL that Sparkle was expecting on clicking of the button earlier and on not finding it gave above error message.

One of the many and simplest (in my opinion best way too) in which we can let Sparkle knows of appcast location is via app's Info.plist.

Upload the sample appcast XML file that you downloaded from Sparkle to your favorite online location and then add the following key-value pair in Info.plist

key - SUFeedURL
value - URL of XML file which you uploaded online
Enter fullscreen mode Exit fullscreen mode

I have uploaded Sparkle's sample XML file to S3. My Info.plist looks like this with:
Alt Text

Go ahead build and run the app. Click on the "Check for Updates" button now. You see an alert that a new version of your app is available (similar to the screenshot below)

Alt Text

If you see an alert like above πŸ‘†

then

Bazinga

Congrats. You have successfully integrated Sparkle in your app.

PS: Any default configuration like appcast URL location should be configured in Info.plist instead of hard coding in code. I added below line

updater?.feedURL = URL(string: "some mystery location")
Enter fullscreen mode Exit fullscreen mode

in the code to explain concept of appcast location. If you notice, even though we are not setting any valid URL updater?.feedURL, Sparkle is still able to get info on app version because Sparkle is getting it from Info.plist. Go ahead and delete that line and Sparkle will still work as long as appcast location is present in Info.plist

Pro tip: If you don't see an alert, then one of the first things you need to check and make sure is that your XML URL is public. This also goes for future debugging as well. For e.g. In Amazon S3, every time you upload a new version of the file (appcast XML), by default URL goes from being public to non-public and we have to make the file public again manually. Knowing this can save a lot of time later trying to debug why the app is unable to find the new version

Stay tuned for the next part, in which we will create our appcast XML and see some of the ways to customize Sparkle to enhance user experience. We will also go through a full cycle of releasing the first version, in which we will see how to correctly code sign app which has Sparkle framework so that it is accepted for Notarization, adding a new feature in next version and updating our current version with a newer version via Sparkle.

It's always a good idea to take a few mins break when working at a stretch. It helps with focus when we come back to the task at hand. If you want to take a few mins to break before your next task, then let me leave you with an interesting trivia

πŸ’‘ Ever wondered why US Government πŸ‡ΊπŸ‡Έ is also referred to as Uncle Sam? πŸ€” It's named after a meat packer Samuel Wilson. Go ahead and take a 2 mins break to read on the history of Uncle Sam

Top comments (14)

Collapse
 
jonpdw profile image
Jonathan de Wet

I am making my first mac app. Thank you for the tutorial. I enjoyed your writing style, it was very easy to follow. Thanks for the effort you put into this :)

Collapse
 
prashant profile image
Prashant Nigam

Thanks Jonathan πŸ™πŸΌ and I am happy you found the tutorial useful. Best of luck with your first Mac app

Collapse
 
prashant profile image
Prashant Nigam

Hi Jonathan, hope your first macOS app development is going well. I have released the second part of this tutorial series. I wanted to let you know, may help with your development efforts.

Collapse
 
jonpdw profile image
Jonathan de Wet

Hi Parshant, yes I managed to successfully complete my app. Thanks for your help with part 1. I think you have created a good resource for the community.

Thread Thread
 
prashant profile image
Prashant Nigam

Your welcome Jonathan. I am glad to hear that your app is successfully completed.

What does your app do? If it is for the public, then please share the link to it and I would love to check it out. Good luck with the launch.

Thread Thread
 
jonpdw profile image
Jonathan de Wet

It's an app that adds extra features (tagging and filtering) to a mind mapping app called MindNode. mindnode-tags.app/
It's a little redundant now as the official app added a feature that is very similar to what my app does. It was still a fun project to work on.

Collapse
 
jsgurugit profile image
jsguru

Nice article.
Now I am eagerly waiting for the Part 2.

Collapse
 
prashant profile image
Prashant Nigam

Hi there, I just posted the second part. Hope you find it useful.

Collapse
 
jsgurugit profile image
jsguru

Thanks a lot.

Collapse
 
d11r profile image
Dragos Strugar

Hey thanks for the tutorial, liked it. When is the second part coming out?

Collapse
 
prashant profile image
Prashant Nigam

Thanks Dragos. I am glad you liked it. I plan to release second part soon in New Years. I will keep you posted

Collapse
 
d11r profile image
Dragos Strugar

Waiting for the part 2! Please tell us when it comes out.

Collapse
 
prashant profile image
Prashant Nigam

Hi there, I just posted the second part. Hope you find it useful. Thanks for patiently waiting. I will be coming out with third (most probably last in the series) part very soon.

Collapse
 
zeeshansuleman profile image
zeeshan

hello , it was a great tutorial . Please share the third part of series. thanks