# Squirrel: a macOS updater where the server decides which version you get

> Squirrel is a Cocoa framework for updating macOS applications, and its distinguishing idea is that the client does not pick from a feed of versions, it asks the server and takes what the server says. The result is server-driven rollouts, phased deployment and rollback, and the build is inherited wholesale from Chromium's build system.

**Squirrel/Squirrel.Mac** — :shipit: Cocoa framework for updating OS X apps :shipit:

- Repository: https://github.com/Squirrel/Squirrel.Mac
- Stars: 1,692 · Forks: 140
- Language: Objective-C
- License: NOASSERTION
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/squirrel-squirrel-mac

## The server chooses, not the client

Squirrel is an OS X framework focused on making application updates as safe and transparent as updates to a website, and the mechanism behind that promise is a deliberate inversion of the usual update model.

Instead of publishing a feed of versions from which your app must select, Squirrel updates to the version your server tells it to. This allows you to intelligently update your clients based on the request you give to Squirrel. The server can remotely drive behaviors like rolling back or phased rollouts.

That is the whole design, and it is worth contrasting with how most update systems work. The conventional model, a static appcast XML or JSON feed listing every version, makes the client the decision maker: the app compares its version against the feed and takes the newest. That design makes phased rollout awkward, because a client cannot know whether it is in the first five percent, and it makes rollback nearly impossible, because the feed only knows about newer versions.

With Squirrel the client asks, and the server answers with a specific version. The request may include authentication details, custom headers or a request body so that the server has the context it needs to supply the most suitable update. The README makes the requirement explicit: your update request must at least include a version identifier so that the server can determine whether an update for this specific version is required, and it may include other identifying criteria such as operating system version or username to allow the server to deliver as fine grained an update as you would like.

How you include the version identifier or other criteria is specific to the server you are requesting updates from. A common approach is to use query parameters, and the configuration example shows a request with a version query parameter and the application's bundle version.

The primary language is Objective-C, the default branch is main, and the repository is not archived with the last push on 2026-10-15. The most recent tagged release is 0.3.2 from 2017, which is a detail worth holding on to and returning to below.

## Building it with GN, Ninja and gclient

The build system is the first thing anyone evaluating this project encounters, and it is the loudest signal in the repository.

Squirrel builds with GN and Ninja on top of Chromium's `//build` configuration, the same way Electron builds it. A standalone checkout fetches the build files, a pinned clang, GN, Ninja and the third-party libraries with `gclient` from depot_tools, and Xcode must be installed for the macOS SDK, `swiftc` and `xctest`.

```sh
git clone https://github.com/Squirrel/Squirrel.Mac.git
cd Squirrel.Mac
cp standalone.gclient .gclient
gclient sync

gn gen out/Default
ninja -C out/Default
script/test out/Default
```

The gclient sync step reads DEPS and checks out build/, buildtools/, tools/clang/, third_party/llvm-build/, third_party/ninja/ and vendor/, all of which git ignores. So the repository you clone is small and the actual build inputs arrive from Chromium's own dependency management, at revisions pinned in DEPS.

The default Ninja target builds Squirrel.framework with ShipIt in its Resources, ReactiveObjC.framework, Mantle.framework and SquirrelTests.xctest, and script/test runs the tests with Xcode's xctest. An optimised build is `gn gen out/Release --args='is_debug=false'`, target_cpu selects the architecture as "arm64" or "x64", and mac_deployment_target is 11.0 by default, set in .gn.

That build configuration is the practical cost of the project, and it is not small. A team that does not already build with Chromium's //build, as Electron does, is taking on a clang checkout, a GN configuration, a DEPS file and a gclient workflow to produce three Objective-C frameworks. The upside of the same choice is that a team that already has that infrastructure, which means an Electron shop, can check this repository out anywhere in its tree, check the libraries under this repository's vendor/ directory, and depend on those targets directly, because BUILD.gn only uses paths relative to itself and templates from //build.

That reuse argument is why the project is built this way, and it is a good argument for the audience the project has.

## Adopting it: three frameworks, no embedding

The adoption section is four steps and one important negative.

Build Squirrel.framework, ReactiveObjC.framework and Mantle.framework as above, or from your own GN build. Link Squirrel.framework and copy all three frameworks into your application's Frameworks directory. Squirrel does not embed its dependencies itself. And ensure your application's Runpath Search Paths, `LD_RUNPATH_SEARCH_PATHS`, includes the directory the three frameworks are copied into.

That last point is where integration usually goes wrong, and it is a classic macOS dynamic linking problem. If the frameworks are copied into the app bundle but the runpath does not include that directory, the app launches and then fails to find a symbol at load time, and the error message points at the framework rather than at the configuration. The build system being Chromium's is unrelated to this, and the requirement is the same one you would meet with any set of manually embedded frameworks.

The three dependencies are ReactiveObjC and Mantle, which gclient sync checks out under vendor/ at the revisions pinned in DEPS, and the build turns them into frameworks next to Squirrel's. The README says if your application already uses either, make sure it uses the same version as Squirrel, which is the standard warning for a framework that links a reactive library: two copies of ReactiveObjC in one process is a class of bug that is very hard to diagnose.

There is a fourth dependency that behaves differently. Binary delta support compiles Sparkle's BinaryDelta sources and the bsdiff it vendors straight out of a third checkout, vendor/Sparkle, pinned to a Sparkle release tag, currently 2.9.5. Nothing of Sparkle is linked as a framework and applications need not ship it. To move the pin, you change sparkle_revision in DEPS, run gclient sync, build, run the tests, and commit, and the files involved are listed in filenames.gni.

That is a well-behaved arrangement. Sparkle is used as a source dependency rather than a linked one, so the delta code is compiled into your app and you carry no Sparkle runtime, but you do carry the obligation to re-pin and re-verify when you want a newer Sparkle.

The tests additionally use Quick, Nimble and OHHTTPStubs, also pinned in DEPS and built from source by SquirrelTests/BUILD.gn.

## Wiring it up, and the signals it gives you

Once Squirrel is added to your project you configure and start it, and the configuration example is short enough to read in full.

```objc
#import <Squirrel/Squirrel.h>

- (void)applicationDidFinishLaunching:(NSNotification *)notification {
	NSURLComponents *components = [[NSURLComponents alloc] init];

	components.scheme = @"https";
	components.host = @"mycompany.com";
	components.path = @"/myapp/latest";

	NSString *bundleVersion = NSBundle.mainBundle.sqrl_bundleVersion;
	components.query = [[NSString stringWithFormat:@"version=%@", bundleVersion] stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet]

	self.updater = [[SQRLUpdater alloc] initWithUpdateRequest:[NSURLRequest requestWithURL:components.URL]];

	// Check for updates every 4 hours.
	[self.updater startAutomaticChecksWithInterval:60 * 60 * 4];
}
```

The mechanism is an SQRLUpdater constructed with an update request, and a polling interval. Squirrel is indifferent to the request the client application provides for update checking, and it adds `Accept: application/json` to the request headers because Squirrel is responsible for parsing the response. That last detail is the contract: your server must answer in JSON, and the header makes sure the server knows which format to use.

Squirrel will periodically request and automatically download any updates, and when your application terminates, any downloaded update will be automatically installed. The install is tied to termination because a macOS application cannot replace its own bundle while running. If you do not want to wait, you can manually terminate the app to begin the installation process immediately.

The notification model is reactive, which follows from the ReactiveObjC dependency. To know when an update is ready to be installed, you subscribe to the `updates` signal on SQRLUpdater:

```objc
[self.updater.updates subscribeNext:^(SQRLDownloadedUpdate *downloadedUpdate) {
    NSLog(@"An update is ready to install: %@", downloadedUpdate);
}];
```

Once an update available notification has been received, you can present an interface informing the user about the update and offering the ability to install it, which is where the library stops and your application starts.

The README also says the installer is designed to be fault tolerant and to ensure that any updates installed are valid, which is the property that makes the model safe. An updater that can leave an application in a broken state is worse than no updater, and the fault tolerance requirement is what lets the server-side decision be trusted.

## The maintenance question, stated plainly

There is one fact about this project that any adopter needs to weigh, and it is the gap between the two dates in the repository metadata.

The last push was on 2026-10-15. The most recent tagged release is 0.3.2, from 2017-08-18, preceded by 0.3.1 from 2017-07-20 and 0.3.0 from 2016-11-10. The repository is not archived, so the code is being worked on, and the framework version is 0.3.2, which is a pre-1.0 version on a nine-year-old tag.

A pre-1.0 version number is meaningful here in a specific way. Under semantic versioning, 0.x means no stability guarantee, and for a library that is consumed by embedding frameworks into an application, the interface is what you are actually depending on. The README documents SQRLUpdater, the `updates` signal, sqrl_bundleVersion and the initializer, and that is the surface you link against. Whether that surface has changed since 0.3.2, and whether it will change in the next release, is not something the README says.

So the accurate statement is: this is a project with current activity on the branch and old release tags, where the code in the repository may be newer than what most people have integrated, and where the version you depend on is not obviously the version the maintainer is working on.

The licence is listed as a custom one that GitHub cannot classify, with a LICENSE file at the root of the repository. For a framework you embed into a commercial macOS application, that file is the one to read before you link it, and it is worth having whoever handles licensing in your organisation do it rather than assuming, since a framework distributed as source under a custom licence may carry terms that differ from the usual permissive defaults.

None of this makes the project a bad choice. The design of server-driven updates is still the right design for a macOS application that needs phased rollout and rollback, and the fault-tolerant installer is the hard part that somebody has already solved. It does mean you should read the LICENSE file, check whether the code in main has moved past the 0.3.2 tag, and decide whether you are building against a release or against a branch.

## Squirrel against Sparkle and the built-in alternatives

The comparison with Sparkle has to start from the fact that Sparkle is already in the dependency tree, because Squirrel vendors its BinaryDelta sources.

Sparkle is the established macOS update framework, and it is designed around the appcast model: the application publishes a feed, the client reads it, and the client decides. That is the model Squirrel is explicitly rejecting in favour of a server that answers a request with a specific version.

The difference in approach therefore comes down to where the decision lives. With Sparkle, the rollout strategy is a property of the feed, and a phased rollout means publishing different feeds or version identifiers to different clients, and a rollback means publishing a version identifier that points back. With Squirrel, the strategy is server logic, so a rollout percentage is a query, a cohort is a request header, and a rollback is the server changing its answer.

The cost of that difference is that Squirrel requires you to write and operate a server endpoint. Sparkle requires a static file. For a small application with a handful of users, that is a meaningful amount of extra infrastructure for a feature you may not need, and it is why Squirrel describes itself as being for the case where the server has the context to supply the most suitable update.

The other comparison is with the platform's own mechanisms. The App Store handles updates for sandboxed Mac App Store applications without any of this, and macOS itself has a Sparkle-adjacent path for apps distributed outside the store. If your application is in the Mac App Store, all of this is irrelevant, and if it is not, then you are in the same position as any developer shipping a signed and notarised app outside the store, and the choice is between a framework you integrate and one you adopt wholesale.

So the deciding question is about control. If you need to decide per client which version it receives, and you are willing to run the endpoint that makes that decision, Squirrel gives you something the appcast model cannot. If you want a static feed and one-click delivery, Sparkle is the smaller commitment, and the delta support Squirrel already borrows from it is the part you would lose.

## Conclusion

Adopt Squirrel if you ship a macOS application and want to control which version each client receives, including phased rollout and the ability to roll back, and you are prepared to build inside Chromium's build system. Do not adopt it if you want a drop-in updater, because the integration is a set of frameworks you link, copy and configure yourself. Verify first that the pinned revision of Sparkle for binary delta support still applies to your release artefacts, that your app's Runpath Search Paths are set up for the three frameworks, and that the deployment target of macOS 11.0 matches the machines you ship to.

## FAQ

### What makes Squirrel different from a conventional appcast updater?

Instead of publishing a feed of versions from which the app must select, Squirrel updates to the version the server tells it to. The request can include authentication details, custom headers or a body, so the server can drive phased rollouts and roll back, and the client adds Accept: application/json because Squirrel parses the response.

### How do I build Squirrel.Mac?

Clone the repository, copy standalone.gclient to .gclient and run gclient sync to fetch the Chromium build files and pinned dependencies, then run `gn gen out/Default`, `ninja -C out/Default` and `script/test out/Default`. Xcode must be installed for the macOS SDK, swiftc and xctest.

### What do I need to do to adopt Squirrel in an app?

Build Squirrel.framework, ReactiveObjC.framework and Mantle.framework, link Squirrel.framework and copy all three into your application's Frameworks directory, since Squirrel does not embed its dependencies, and make sure LD_RUNPATH_SEARCH_PATHS includes that directory.

### Does Squirrel use Sparkle, and does my app need to ship it?

Binary delta support compiles Sparkle's BinaryDelta sources and the bsdiff it vendors from a third checkout, vendor/Sparkle, pinned to a Sparkle release tag, currently 2.9.5. Nothing of Sparkle is linked as a framework and applications need not ship it, and moving the pin means changing sparkle_revision in DEPS and rebuilding.

## Sources

- [Issues](https://github.com/Squirrel/Squirrel.Mac/issues)
- [README](https://github.com/Squirrel/Squirrel.Mac/blob/main/README.md)
- [Releases](https://github.com/Squirrel/Squirrel.Mac/releases)
- [Squirrel/Squirrel.Mac on GitHub](https://github.com/Squirrel/Squirrel.Mac)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/squirrel-squirrel-mac
