Xcode Project Setup to Build, Embed, Codesign, Notarize, and Use the Sparkle XPC Services

In the previous post, I talked about how to download the “modern” Sparkle updater branch called ui-separation-and-xpc that allows you to update sandboxed apps and migrate your code to use the new types.

This time, we’re going to use the actual XPC services that do the grunt work.

Remember: Sparkle does not require the XPC services to work. If you embed them, they are used. This is necessary for Sandboxed apps. All the other kinds of app can skip using the XPC services if they want.

This is the third post in a series:

  1. How to migrate to the new Sparkle XPC branch
  2. How to toggle use of XPC service (Spoiler: you don’t have to do anything but bundle the XPC services in your app)
  3. How to build, embed, and notarize the XPC services in your app (this post)

Prepare App Sandbox Entitlements

I assume your app is sandboxed. Otherwise, you could still use the traditional Sparkle updater version.

Your app will download the update feed from the web. Make sure you add the com.apple.security.network.client entitlement. Add this manually or head to the “Capabilities” tab of your app target, expand the “App Sandbox” settings, then enable “Outgoing Connections (Client)”.

Alternatively, you can use the SparkleDownloader XPC service (its filename is org.sparkle-project.Downloader.xpc). But I didn’t test this, because I need the networking entitlement to display the About.html file in my app anyway for some reason. When we embed the XPC services in the next steps, keep in mind to include this extra service.

Build the XPC services

When you build your app with the new Sparkle framework, the XPC services will not build automatically.

Let’s have a look at TableFlip’s $(BUILT_PRODUCTS_DIR), where you will find a flat list of all build products for the app project:

$ l /Users/ctm/Library/Developer/Xcode/DerivedData/TableFlip-fggcerpwydmlxkbphymfxiredpny/Build/Products/Debug
total 1032
-rwxr-xr-x  1 ctm  staff   441K Jun 21 09:57 Autoupdate
drwxr-xr-x  7 ctm  staff   224B Jun 21 09:57 Export.framework
drwxr-xr-x  7 ctm  staff   224B Jun 21 09:57 FastSpringStore.framework
drwxr-xr-x  7 ctm  staff   224B Jun 21 09:57 LatexExport.framework
drwxr-xr-x  7 ctm  staff   224B Jun 21 09:57 Sparkle.framework
drwxr-xr-x  7 ctm  staff   224B Jun 21 09:57 State.framework
drwxr-xr-x  4 ctm  staff   128B Jun 21 09:57 StateTests.swiftmodule
drwxr-xr-x  3 ctm  staff    96B Jun 21 09:57 TableFlip.app
drwxr-xr-x  4 ctm  staff   128B Jun 21 14:21 TableFlip.swiftmodule
drwxr-xr-x  3 ctm  staff    96B Jun 21 09:57 TableFlipHelp.help
drwxr-xr-x  4 ctm  staff   128B Jun 21 09:57 TableFlipTests.swiftmodule
drwxr-xr-x  7 ctm  staff   224B Jun 21 09:57 TableModel.framework
drwxr-xr-x  3 ctm  staff    96B Jun 21 09:57 Updater.app
drwxr-xr-x  3 ctm  staff    96B Jun 21 09:57 include
-rw-r--r--  1 ctm  staff    70K Jun 21 09:57 libbsdiff.a

Next to all the TableFlip-related stuff, it contains Sparkle.framework and its dependencies Updater.app and Autoupdate. It does not include the XPC services.

That’s because only Sparkle.framework is a “Target Dependency” of the app; and if you look at the build phases of the Sparkle.framework itself, the “Autoupdate” and “Installer Progress” targets of the Sparkle project are in turn its dependencies.

The current version of the install instructions assumes you have built Sparkle from the command line using make release. That would build the XPC services for you, too. If I cannot use a dependency manager like Carthage, I prefer to build the framework and XPC services as part of the app build process; the previous post prepared the project for just that, and that’s why the XPC services haven’t been built, yet.

The fix is simple: add more target dependencies to build the XPC services.

Heads up for the lazy ones: Do not just use the “Distribution” target. Like the make command, it builds the framework, the services, and all example apps. When you try to export your own app afterwards from Xcode, you will end up with a generic Xcode archive, but not the usual archive that you can export as an app. That’s because Xcode will try to bundle your own app and the Sparkle test app together, and that doesn’t work out, except with a generic archive that’s of no use.

screenshot of Xcode
Add the XPC services from the Sparkle sub-project as target dependencies
  • Show your app project settings
  • Select the app target
  • Select the “Build Phases” tab
  • Expand “Target Dependencies”
  • Remove Sparkle.framework from the dependencies list
  • Add (“+”) new dependencies and select the XPC services from the “Sparkle” sub-project: do include org.sparkle-project.InstallerConnection.xpc, org.sparkle-project.InstallerLauncher.xpc, org.sparkle-project.InstallerStatus.xpc, and consider org.sparkle-project.Downloader.xpc if you need it.

Build your app, then check the directory again:

$ l /Users/ctm/Library/Developer/Xcode/DerivedData/TableFlip-fggcerpwydmlxkbphymfxiredpny/Build/Products/Debug
-rwxr-xr-x  1 ctm  staff   441K Jun 22 11:29 Autoupdate
drwxr-xr-x  7 ctm  staff   224B Jun 22 11:29 Export.framework
drwxr-xr-x  7 ctm  staff   224B Jun 22 11:29 FastSpringStore.framework
drwxr-xr-x  7 ctm  staff   224B Jun 22 11:29 LatexExport.framework
drwxr-xr-x  7 ctm  staff   224B Jun 22 11:29 Sparkle.framework
drwxr-xr-x  7 ctm  staff   224B Jun 22 11:29 State.framework
drwxr-xr-x  3 ctm  staff    96B Jun 22 11:30 TableFlip.app
drwxr-xr-x  4 ctm  staff   128B Jun 22 11:29 TableFlip.swiftmodule
drwxr-xr-x  3 ctm  staff    96B Jun 22 11:29 TableFlipHelp.help
drwxr-xr-x  7 ctm  staff   224B Jun 22 11:29 TableModel.framework
drwxr-xr-x  3 ctm  staff    96B Jun 22 11:29 Updater.app
drwxr-xr-x  3 ctm  staff    96B Jun 22 11:29 include
-rw-r--r--  1 ctm  staff    70K Jun 22 11:29 libbsdiff.a
drwxr-xr-x  3 ctm  staff    96B Jun 22 12:29 org.sparkle-project.Downloader.xpc
drwxr-xr-x  3 ctm  staff    96B Jun 22 11:29 org.sparkle-project.InstallerConnection.xpc
drwxr-xr-x  3 ctm  staff    96B Jun 22 11:29 org.sparkle-project.InstallerLauncher.xpc
drwxr-xr-x  3 ctm  staff    96B Jun 22 11:29 org.sparkle-project.InstallerStatus.xpc

This means it worked!

Embed the XPC Services

As for embedding, the Sparkle.framework is already embedded in your app (we did that in the previous post); the XPC services aren’t, though.

Still in your app’s build phases, add another build phase and select “New Copy Files Phase” from the dropdown.

  • Rename the new build phase to “Embed XPC Services”
  • Select ”XPC Services” as its destination
  • Add files (“+”) and select the services with “Installer” in their name: org.sparkle-project.InstallerConnection.xpcorg.sparkle-project.InstallerLauncher.xpcorg.sparkle-project.InstallerStatus.xpc

If you want to embed the SparkleDownloader as well, this is the place and time to do so.

Codesigning and Hardened Runtimes

Sparkle comes with a script that does code-signing and runtime hardening for you. It is located at bin/codesign_embedded_executable inside the Sparkle framework project directory.

For reference, these are the original instructions, assuming you sign the build products once and then simply embed them in your app:

./bin/codesign_embedded_executable "Developer ID Application: XXX" XPCServices/*.xpc
./bin/codesign_embedded_executable "Developer ID Application: XXX" ./Sparkle.framework/Versions/A/Resources/Autoupdate
./bin/codesign_embedded_executable "Developer ID Application: XXX" ./Sparkle.framework/Versions/A/Resources/Updater.app/

As announced at the beginning, here’s my take on the instructions to embed and sign everything as part of your build:

  1. Figure out the script location
  2. Automate the signing identity selection
  3. Figure out the framework and XPC bundle paths

1. Location of the script

Assuming that you put Sparkle in Extern/Sparkle/ relative to your project directory, then the script will be located at

$(PROJECT_DIR)/Extern/Sparkle/bin/codesign_embedded_executable

2. Codesign identity

In practice, this part worked great for me since hardening the runtime of my apps:

IDENTITY="${CODE_SIGN_IDENTITY}"
if [ "$IDENTITY" == "" ]
then
    # If a code signing identity is not specified, use ad hoc signing
    IDENTITY="-"
fi
codesign --verbose --force --deep -o runtime --sign "$IDENTITY" <TARGET_BUNDLE>

We’ll be using this approach to getting to the IDENTITY variable.

3. Paths of the framework and XPC services

You can either code-sign and harden the runtimes before the bundles are embedded in your app, or afterwards. I prefer to sign after the bundles are embedded because other scripts on the web did the same, but it doesn’t seem to matter.

When you want to sign before embedding, use $(BUILT_PRODUCTS_DIR)/Sparkle.framework/ and $(BUILT_PRODUCTS_DIR)/*.xpc. The base location of build products, including your app, is $(BUILT_PRODUCTS_DIR). We looked at the contents of this before. Xcode assembles the app from the parts in here.

When you want to sign after embedding, you can find the framework and XPC service inside your app bundle. For example, CoolApp.app/Contents/Frameworks/Sparkle.framework exists, and CoolApp.app/Contents/XPCServices/ contains the XPC services. You can use the following variable path combinations: $(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/Sparkle.framework/ and $(BUILT_PRODUCTS_DIR)/$(XPCSERVICES_FOLDER_PATH)/*.xpc. For reference, in case of TableFlip XPCSERVICES_FOLDER_PATH is TableFlip.app/Contents/XPCServices.

With the paths figured out, let’s add the script!

  • Go to your app target’s settings if you aren’t there anymore
  • Select the “Build Phases” tab again
  • Add (“+”) a build phase and select “New Run Script Phase” this time
  • Rename the build phase to “Code-sign Sparkle” or similar
  • Drag and drop this build phase after both the “Embed XPC Services” build phase we created above, and the default “Embed Frameworks” build phase

Then paste the following into the script text view; we’ll be signing after the stuff is embedded, remember:

# Shorthand for the script
alias dosign="${PROJECT_DIR}/Extern/Sparkle/bin/codesign_embedded_executable"

# Code Signing identity
IDENTITY="${CODE_SIGN_IDENTITY}"
if [ "$IDENTITY" == "" ]
then
# If a code signing identity is not specified, use ad hoc signing
IDENTITY="-"
fi

# Shorthand for the Sparkle.framework
SPARKLE_PATH=${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Sparkle.framework

dosign "$IDENTITY" "${BUILT_PRODUCTS_DIR}/${XPCSERVICES_FOLDER_PATH}"/org.sparkle-project.*.xpc
dosign "$IDENTITY" "${SPARKLE_PATH}/Versions/A/Resources/Autoupdate"
dosign "$IDENTITY" "${SPARKLE_PATH}/Versions/A/Resources/Updater.app/"
dosign "$IDENTITY" "${SPARKLE_PATH}"

I found I needed to add a re-signing step for the Sparkle framework itself since I sign after the framework was embedded and already signed during that step, but then its contents were modified by the script. You will probably not need the last line if you signed before embedding the framework and services.

Building and testing

Build your app. There should be no errors.

When you build your app in release mode for distribution using the “Build > Archive” command and export the result, verify that the hardening of runtimes and the signing of bundles worked as expected:

$ spctl --assess -vv TableFlip.app
/path/to/TableFlip.app: accepted
source=Developer ID
origin=Developer ID Application: Christian Tietze (FRMDA3XRGC)

That’s good.

Note that spctl will reject your debug-mode build products, so you never know until you export the final app.

Also note that you can verify signing all you like: if an embedded (!) helper app or XPC service is not signed or notarized properly, only a test run on a clean system (or a Guest user on your own computer) will reveal problems when you download the file from the internet. Gatekeeper will not show some notarization errors for files that weren’t downloaded from the web.

This bit me before, so please make sure to test the download for a Guest user before you make it public.

So far, the updates seem to work in release mode. I have to archive the app first; when I run it from Xcode in Debug mode, the feed will not be downloaded and no update dialog presented. That’s a bummer. It’s most likely an error on my side: other people had great success with testing updates when they run their app from Xcode.

I will have to test if running make release outside of Xcode solves the problem.

How Do You Activate Sparkle’s XPC Services?

The Sparkle XPC fork is even better than I would’ve imagined!

As I mentioned in the previous post about Sparkle’s XPC branch, you can switch to the new framework version and the new types without having to change much else. The new API is very similar to the old one. If you switch to the work-in-progress ui-separation-and-xpc branch, then you can continue to use CocoaPods or Carthage if all you want is the new API.

Only when you want to use the XPC services will you have to build the Sparkle build products yourself.

That means the Sparkle framework does not require the XPC services to work. They are optional. Unless you want to use them, you won’t notice any difference.

Huh.

But how do you set up your app to use the XPC services if you want to? Is there a new initializer or delegate callback or anything? If disabling XPC services is not required, then enabling must be, right?

Turns out Sparkle does all the decision making for us. In the Sparkle.xcodeproj, search for occurrences of the SPUXPCServiceExists function. Then you’ll see where Sparkle checks if an XPC service is present in its host bundle, aka your app:

  • If no XPC services are bundled with your app, Sparkle more or less behaves like it used to for a decade and more.
  • If you bundle the XPC services with your app, Sparkle will utilize them instead.

This is great, because that means you have zero configuration to do!

I was puzzled for a while after I set up the basics yesterday. What’s the next step? Bundle the XPC services with the app, and then what? Nothing: There is no step three!

I’m happy to see that the Sparkle project was designed to be this simple to use.

The SPUXPCServiceExists function is also pretty simple: see code for SPUXPCServiceInfo.m on GitHub.


This was the second post in a series:

  1. How to migrate to the new Sparkle XPC branch
  2. How to toggle use of XPC service (this post)
  3. How to build, embed, and notarize the XPC services in your app

How to Migrate to the New Sparkle Updater XPC Branch

This is the first post in a series:

  1. How to migrate to the new Sparkle XPC branch (this post)
  2. How to toggle use of XPC service (Spoiler: you don’t have to do anything but bundle the XPC services in your app)
  3. How to build, embed, and notarize the XPC services in your app

The standard solution to provide app updates outside the Mac App Store is to use the amazing Sparkle framework.

The releases of Sparkle don’t support sandboxed applications well, though, so the core maintainer Kornel Lesiński (@kornelski) is working on a more secure fork of Sparkle next to the existing one. He’s maintaining the regular Sparkle, and the fork. The fork features various XPC services to deal with Gatekeeper security configurations and Sandbox entitlements in a proper way. (I have to phrase it in such a vague way because I have no clue about the details, yet. Explanations are very welcome in the comments!)

The future of Sparkle is in a combination of the new XPC branch with the latest additions to the core branch, like EdDSA. But the future of Sparkle 2.0 is the XPC-based refactoring.

So how do you switch from regular Sparkle 1.x to the ui-separation-and-xpc branch if you want to familiarize yourself with the new project layout and types?

  1. Switch to the new project,
  2. migrate existing code to use new classes,
  3. use the XPC services where appropriate.

I want to cover steps (1) and (2) here. I’m still figuring out (3).

You do not have to use the XPC services. Even though the docs currently state that this is optional, I find it hard to really figure out what’s going on. That’s why I’m writing about all this: to expand the available corpus of information.

So when we focus on steps (1) and (2), all we really do is migrate to the new types; we will still be using the Sparkle.framework build product like before, and that’s it. Nothing more to achieve today.

Checkout the new branch

Carthage and CocoaPods currently don’t work with the new branch’s framework if you want to build the various XPC services.

They do work if all you want is the updated Sparkle.framework. In that case, go to the next step.

You could download the repo content manually to experiment with the new stuff. For a more useful integration, we’re going to use a git submodule, though, to be able to pull changes easily.

$ cd /path/to/your/xcode-project
$ mkdir -p ./Extern
$ cd ./Extern/
$ git submodule add -b ui-separation-and-xpc https://github.com/sparkle-project/Sparkle.git
$ open .                   # Open directory in Finder to drag & drop the project file

This will check out Sparkle at the ui-separation-and-xpc immediately. Then drag the Sparkle.xcodeproj into your app project to access its build products. I usually drop sub-projects like this inside the app project’s “Framework” group.

You might want to review your build schemes in Xcode now; I hide all the Sparkle-related schemes from the drop-down by unchecking the “Show” checkbox to keep the scheme dropdown focused on my app.

screenshot of Xcode
Sparkle.framework build products in Xcode 10 as a sub-project

If you had a reference to Sparkle.framework in your Xcode project before, delete that. We’re going to be using the one of Sparkle.xcodeproj, which you’ll find inside its “Products” group.

If you never embedded an external framework into your app in your life: Locate the framework in the sub-project by expanding the folders/groups leading to it. Click on your app project, then on the app target in the project viewer. Select the “General” tab, scroll down to “Embedded Binaries”. Then drag and drop the Sparkle.framework from the project navigator to the left into the “Embedded Binaries” list. This will link the app with the framework and include the framework in the final app. (There’s an old video that show the old Xcode UI but is otherwise accurate.)

Migrate old Sparkle types

Ye olde SUUpdater is no more. It’s deprecated and a wrapper around the new types. It was commonly used inside Interface Builder Nibs to auto-start the updating mechanism in the background from the MainMenu.xib. For this purpose, you now will use SPUStandardUpdaterController.

The actual functionality of the SUUpdater is now put inside SPUUpdater. Yep, after a decade Sparkle now adopted three letter type prefixes in Objective-C! When things settle down, there’ll be plenty of room for NS_SWIFT_NAME pull requests.

If you used Cocoa bindings in your settings or main menu to toggle auto-updating via automaticallyChecksForUpdates, or display the date of the last update in a label via lastUpdateCheckDate, you need to rewire your bindings! SPUStandardUpdaterController is not KVO compliant for either. It does expose the checkForUpdates(_:) method that you can bind to menu items or buttons in Interface Builder, but the rest is part of SPUUpdater, not SPUStandardUpdaterController. You need to drill into the controller’s updater property which actually is of the SPUUpdater type. So the model path of the binding changes from e.g. self.automaticallyChecksForUpdates to self.updater.automaticallyChecksForUpdates. Adjust the other paths similarly.

With these changes in place, you should be ready to roll!

Changes to the object lifecycles in Sparkle

Note that SPUUpdater, unlike the old SUUpdater, does not cache instances! Previously, you could just call the default initializer anywhere inside the same app bundle and get the same object. It was like a singleton in that regard. But now the SPUUpdater has no such mechanism. Neither has SPUStandardUpdaterController.

You are allowed to instantiate multiple SPUStandardUpdaterController objects in different Nib files. It is sufficiently safe to do so. They don’t perform any black magic: all they do is set up the updater object and start the update process. There should be no trouble having multiple instances in memory – but I haven’t checked this, because I don’t find this use case particularly interesting. I only use the controller object inside of preference windows and so far haven’t experienced any clashes or duplicate update requests.

Conclusion

If you customize your update process a lot – and this 2.0 preparation fork of Sparkle allows a ton of customization, including cusom updater UI! –, then you will likely want to keep a shared/global/Singleton updater object around, because it is tied to a SPUUpdaterDelegate, and maybe even to one of the “driver delegates” that are notified about download progress. Indeed I set this up once in my AppDelegate and then save this as a variable that I can access from anywhere else in the app. (Call it Singleton, call it a global variable, call it a static property – it’s the same mechanism.) I essentially replicate the shared object model of the old Sparkle framework version in my own code.

In fact, I’ll be using this more heavily in the WordCounter app. I’m splitting the WordCounter into multiple helper apps at the moment, and I want all helper and XPC apps to use shared preferences to determine if updates are to be fetched using the “beta update” or the “stable update” feed. These kind of customizations work well with SPUUpdaterDelegate, but I need to use it everywhere and make sure that no Nib creates a default controller and default updater with the default settings. All this is less convenient, but only a little bit.

Sparkle’s ui-separation-and-xpc branch works fine out-of-the-box, and is still super simple to integrate!

The new customization options make it very flexible. The migration docs are not very instructive, but I’ll keep you posted on updates. Meanwhile, the Objective-C header files provide a lot of essential information:

/*!
 A controller class that instantiates a SPUUpdater and 
 allows binding UI to it.

 This class can be instantiated in a nib or created using
 initWithUpdaterDelegate:userDriverDelegate:. The
 controller's updater targets the application's main bundle
 and uses Sparkle's standard user interface. Typically, this
 class is used by sticking it as a custom NSObject subclass
 in an Interface Builder nib (probably in MainMenu).

 The controller creates an SPUUpdater instance and allows
 hooking up the check for updates action and menu item
 validation. It also allows hooking up the updater's and
 user driver's delegates.

 This controller class may not be valuable to you if:

 * You want to control or defer the instantiation of an
   SPUUpdater, or don't want to be tied into a nib's
   instantiation, or don't want to use a nib
 * You want to target a bundle that's not the main bundle
 * You want to provide a custom user interface, or perhaps
   one that provides little-to-none
 * You don't want to use a convenience class that provides
   very little glue ;)

  */
@interface SPUStandardUpdaterController : NSObject
/*!
 The main API in Sparkle for controlling the update mechanism.

 This class is used to configure the update parameters as well as manually
 and automatically schedule and control checks for updates.
 */
SU_EXPORT @interface SPUUpdater : NSObject

/*!
 Initializes a new SPUUpdater instance

 This does not start the updater. To start it, 
 see -[SPUUpdater startUpdater:]

 Note that this is a normal initializer and doesn't implement 
 the singleton pattern (i.e, instances aren't cached, 
 so no surprises).

 This also means that updater instances can be deallocated, 
 and that they will be torn down properly.

 Related: See SPUStandardUpdaterController which wraps a 
 SPUUpdater instance and is suitable for instantiating in nib files

 @param hostBundle The bundle that should be targetted for updating. This must not be nil.
 @param applicationBundle The application bundle that should be relaunched and waited for termination. Usually this can be the same as hostBundle. This may differ when updating a plug-in or other non-application bundle.
 @param userDriver The user driver that Sparkle uses for user update interaction
 @param delegate The delegate for SPUUpdater. This may be nil.
 */
- (instancetype)initWithHostBundle:(NSBundle *)hostBundle
                 applicationBundle:(NSBundle *)applicationBundle 
                        userDriver:(id <SPUUserDriver>)userDriver 
                          delegate:(id<SPUUpdaterDelegate> _Nullable)delegate;

Use Shared NSUserDefaults for XPC and Interface Builder Nibs

The default Interface Builder approach of using a “Shared User Defaults Controller” object breaks down if your app preferences are not stored in the standard place.

Both my apps The Archive and the Word Counter for Mac are comprised of multiple executable applications. For The Archive, it’s the main app and the Quick Entry popup window. They share some settings, like which theme and font size is used. To share these settings, I rely on NSUserDefault (or just UserDefaults in Swift now). I cannot use the UserDefaults.standard, though, because that is tied to the currently running app’s bundle ID. In the case of the main app, it’s the ID of the main app; but for the Quick Entry helper – or any helper app –, it’s the helper’s bundle ID. This way, the defaults dictionaries are not shared.

I solve it this way:

  1. Use a custom App Group for the related apps (main app + helper app)
  2. Use UserDefaults(suiteName: "the.app.group.name") in all apps
  3. Replace Interface Builder’s “Shared User Defaults Controller” with a custom one

App Group

To set up an app group,

  • go to the project settings in Xcode,
  • select the “Capabilities” tab,
  • enable “App Groups”
  • add a new entry there for your app’s bundle ID of the format $(TeamIdentifierPrefix)com.example.app-bundle-id.prefs (I like the .prefs suffix, but it’s optional)

Do this for all affected app targets in your project.

Custom UserDefaults instance

The simple version is to just initialize a new UserDefaults instance with the written-out version of the App Group you just created:

UserDefaults(suiteName: "XXXYYYZZZA.com.example.app-bundle-id.prefs")

But I in fact use multiple app groups, using the .prefs and .prefs-dev and .prefs-dev-testing suffixes. This way I can experiment with my Xcode DEBUG builds without affecting the real app on my machine that I rely on for a lot of my work!

#if DEBUG
fileprivate var sharedUserDefaults: UserDefaults 
    = UserDefaults(suiteName: "XXXYYYZZZA.com.example.app-bundle-id.prefs-dev")!
fileprivate var sharedUserDefaultsForTesting: UserDefaults 
    = UserDefaults(suiteName: "XXXYYYZZZA.com.example.app-bundle-id.prefs-dev-testing")!
// Testing seam; During testing, this is switched to another instance.
fileprivate var usedUserDefaults: UserDefaults 
    = sharedUserDefaults
#else
fileprivate var sharedUserDefaults: UserDefaults 
    = UserDefaults(suiteName: "XXXYYYZZZA.com.example.app-bundle-id.prefs")!
#endif

extension UserDefaults {
    #if DEBUG
    public static var shared: UserDefaults {
        return usedUserDefaults
    }

    enum Mode {
        case app, test
    }

    internal static func changeUsedUserDefaults(to mode: Mode) {
        usedUserDefaults = {
            switch mode {
            case .app:
                return sharedUserDefaults

            case .test:
                return sharedUserDefaultsForTesting
            }
        }()
    }
    #else
    public static var shared: UserDefaults {
        return sharedUserDefaults
    }
    #endif
}

That’s probably doing more than you did expect! It boils down to exposing a UserDefaults.shared static property that I can use anywhere in the apps.

And during tests, I can replace the regular development preferences (which I often want to keep unchanged between manual testing runs) with another instance entirely that can be wiped and filled with garbage and whatnot. Then I call UserDefaults.changeUsedUserDefaults(to: .testing) in my XCTestCase.setUp implementation.

So this already works for programmatic changes.

How do you employ the result when designing preference panes in Interface Builder’s Nibs, though?

Replacement for the regular NSUserDefaultsController

Interface Builder’s “Shared User Defaults Controller” is of the “object” type and references an instance of NSUserDefaultsController. These are handy because they are Key-Value-Coding compliant. You can bind label texts and checkbox statuses to them easily.

But they are not configurable to point to the UserDefaults suite.

Instead of subclassing NSUserDefaultsController to provide such @IBDesignable capabilities, I reroute the KVO/KVC targets.

Take my base PreferenceViewController class, for example:

class PreferencesViewController: NSViewController {

    /// Controller for `UserDefaults.shared` so that KVO of view components
    /// doesn't use the (wrong) standard user defaults.
    @objc dynamic lazy var userDefaultsController: NSUserDefaultsController 
        = NSUserDefaultsController(defaults: self.defaults, initialValues: nil)
    var defaults: UserDefaults { 
        return UserDefaults.shared 
    }
}

My preference view controller subclass this type. In their Nib files, I set up a label text to display the last time Sparkle updater checked for an update like this:

  • Create label (NSTextField)
  • Below “Value With Pattern”, bind the label’s “Display Pattern Value1” to “File’s Owner” (that’s the view controller)
  • Set “Model Key Path” to self.userDefaultsController.values.SULastCheckTime
  • Set “Display Pattern” to Last Update: %{value1}@ (if you didn’t use “Display Pattern Value1”, you need to use another number here, e.g. value2)

And now I have the convenience of auto-updating labels and other UI items thanks to KVO/KVC plus a shared UserDefaults suite among my helper apps and the main app.

Your preference files will actually be stored in this location, by the way: ~/Library/Group\ Containers/XXXYYYZZZA.com.example.app-bundle-id.prefs/Library/Preferences/XXXYYYZZZA.com.example.app-bundle-id.prefs.plist.

Imposing Bans and App Store Sanctions

Brent Simmons wrote about imposing sanctions by making apps unavailable in certain countries (in his case: Saudi Arabia) in November 2018.

I never thought about the mere possibility of doing so. It’s an intriguing thought: even when politics don’t result in whatever you want, you can always be picky about who you do custom with. It’s a power we have, a power every producer and craftsperson has. Turn down a business for moral reasons.

Naturally, I talked this through with a friend. We both met through our philosophy studies at university, so I knew the discussion would bear fruit. He pointed out that imposing bans on a country level may have unintended consequences. Take China for example, a more recent bogeyman of sorts. Do you want to take your app from Chinese stores to “punish the system”? If so, you’ll also punish the investigative journalists in China, he pointed out. Your actions will have casualties.

Well, what a bummer. The power fantasy is intriguing: you churn out code and you can influence international politics and the world, in a way! – But unless you talk with every customer before their purchase, you’ll end up punishing all the wrong people, too. Collateral damage. I don’t like that a bit.

I don’t know why Brent asked about Saudi Arabia. Because their funding of terrorism? Who funds this? All Saudis? Does your app promote terrorism-funding? Do you really want to punish Saudi Arabia’s government instead of its people? What do you talk about when you talk about a country, really?

So for now I will not be imposing bans on any specific country, even though I could: FastSpring, my e-commerce provider of choice, provides the capabilities to impose filters. Not going to use them any time soon.

Free Amazon S3 and CloudFront Hosting for Students

If you’re a student and apply for GitHub Education, you get a lot of cool stuff for free.

Part of this cool stuff apparently is “aws educate” access, I was told:

GitHub Student Developer Pack members receive up to $110 in bonus AWS credits for a total of $75-$150

This means you can host your indie app downloads on S3 and CloudFront for free for a very long time. I wrote a tutorial on how to set up CloudFront to host downloads and potentially static websites. It details the transition of an existing S3 bucket to the modern access rights management and a cached CloudFront endpoint.

If you’re new to S3/CloudFront, you can start with these simple steps:

  1. Create S3 bucket: https://console.aws.amazon.com/s3/
    • Disable public access (all of it)
    • Create /public directory in the bucket
    • Upload a /public/index.html with a link back to your website
  2. Set up CloudFront cache: https://console.aws.amazon.com/cloudfront/

    You’ll be creating a CloudFront user (“Origin Access Identity”, with access to S3) and a distribution (public URL endpoint) Best create a new CloudFront Distribution and let the setup do all the work:

    • Create Distribution”, then select the “Web” target
    • Origin Domain Name: Select the S3 bucket target you created in step 1
    • Origin Path: /public
    • Restrict Bucket Access: (1) Yes, (2) Create a New Identity
    • Grant Read Permission: Yes
    • (Leave rest in between as is; ensure you use SSL certificate)
    • Default Root Object: index.html (path is relative to the origin path)

That’s it. Wait a while until everything is set up, test the URL, and you should see the contents of your index.html.

From bash to zsh on macOS

In anticipation of macOS 10.15 Catalina, I have changed my shell from bash to zsh. macOS 10.15 will use zsh as the new default, and I was pretty sure that things will break immediately unless I prepare – so I did prepare, and I found the transition very simple.

zsh in iTerm2 with the snazzy theme and pure installed

My old bash prompt didn’t work out of the box, so getting a decent prompt with some color and git repository information, I managed to set up sindresorhus/pure to offer asynchronous (!) info like the pwd’s git metadata.

If you want my setup, install this:

I had to split my old .bash_profile file into an .aliases file that is used by both bash and zsh; similarly, I extracted variable settings (like adjustments to my PATH) into a shared file. Both zsh and bash can “import” other shell files via the source FILENAME program. To split the configuration up and then migrating the old bash stuff this way proved to be very simple and satisfying.

I didn’t want to replace the .bash_profile because I want my dotfiles to be compatible with my webserver and other devices where zsh is not installed.

Browser-Like Navigation in The Archive

This weekend, I released an update to the note-taking app I’m working on for a while called The Archive. This update is pretty big for people not getting updates from the opt-in “Cutting Edge” update channel, because all of a sudden the app allows you to navigate back in time.

The navigation stack (what you’d call browser history available from the navigation buttons in your browser) behaves like this:

  • Select a note from the search result list, and it gets added to the navigation stack.
  • Type into the Omnibar to search. A snaptshot for the history will be created once you change the selection.

Restoration of a historic point in time means the same note you visited is visible again, and the search results are restored completely. Including stuff you might have deleted in the meantime. You could use this to restore notes, if you wanted.

Check out The Archive’s free 60 day trial. Experience this yourself.

The Archive now sports the well-established pattern of showing a back/forward arrow we all know and love from every graphical web browser, ever

It took a while to experiment with the rules for this. What counts as a “historic event” that should be recorded?

If you type a long search term into the app’s Omnibar, it wouldn’t make much sense to add each keystroke to the history. So that’s not a good candidate for a rule.

If the app remembered changes to search results instead of your keystrokes, the history would be less noisy and travelling back in time would be more meaningful, granted. It’s still too weird, though. Let me illustrate what I mean here: if you type to search for Zettelkasten, it’s very likely that the set of search results will stay the same after you typed Zettelk. So the subsequent searches for Zettelk, Zettelka, Zettelkas, Zettelkast, Zettelkaste, and Zettelkasten would not be stored in the history because the search results are the same for all of these (partial) search terms. But is the set of search results the most meaningful thing of the app? Is it what you want to go back to?

Eventually, we settled on getting back to changes of note selection.

If you select a note from the search results, it’s added to the history and you can go back to it. That feels pretty natural already, much like clicking on a link in a web browser results in a different page being shown, so you hit the back button to get to the previous page. We don’t tend to think about this and take it for granted; it’s a clear case.

But what about searches? The Archive sports a feature where it automatically shows the best match of your search result in the editor. Whenevery you follow a link from note A to note B, this is essentially what’s happening under the hood: note B’s title is put into the search, and B is the best match for its title, so it is displayed immediately. This interaction, following links, had to result in putting the note on the history stack as well.

Through this, we initially got adding the best match for a search when the user types for free. And it’s not weird at all. Often the best fit, displayed by the app, end up being what the user interacts with. And by “interact” I don’t mean editing the contents; looking at it is enough oftentimes. You want to get back to the note you just read a minute ago. So we kept this.

Navigating your notes in The Archive is feeling more and more natural with every update. I really like to work inside the app as much as I like working on the app. I take this as a good sign, and I sure hope the app brings delight to the work of others, too!

I cannot wait to see the next big features being implemented and talk to users about them. I really can’t. It’s a pity I’m the bottleneck here, darnit :)

Check out The Archive if you haven’t already and tell me what you think of the latest additions!

embetty Embeds Tweets and YouTube Videos Without the Tracking Code

Found embetty, and now I am reconsidering to embed marked-up Tweets and YouTube videos again. Embetty displays Tweets as proper cards, but without Twitters visitor-tracking code.

Instead of screenshots, they have a demo page.

You need to setup an embetty-server first, though. That’s kind of annoying, but not much of a problem for pros. I wonder if a shared server would do, or if that puts you into weird legal situations again, because the embetty-server proxy now becomes the de facto hub of displayed content but isn’t allowed to according to Twitter and YouTube rules, or something like that.

Python Context Manager Compared to Ruby DSLs

Python has the with keyword that is analogous to using in C#. It is used to manage resource access and wrap a block of code with setup and cleanup stuff. I like it.

Here’s an example adapted from the Python Enhancement Proposal (PEP) 343 that introduced the implementation in Python 2.6:

from contextlib import contextmanager

@contextmanager
def opening(filename):
   f = open(filename)
   try:
       yield f
   finally:
       f.close()

with opening("the/filename") as f:
    for line in f:
        print line

I like it so much because it reminds me of the block-based resource access I’m now used to thanks to Ruby and Swift spoiling me for years. I’m not a fan of repeating imperative sequences between opening/closing file handle code since I know you can do it this way.

I’m still learning Python. That’s why I sometimes try to come up with equivalent Ruby code (which is my reference language of choice to explain new concepts in familiar terms). I noticed that the Python example above sports the yield keywords, which is how blocks are called in Ruby, too. So I wondered what the @contextmanager decorator is useful for, and why the with keyword relies on the result of the expression to respond to the context manager protocol. This protocol is defined through the __enter__ and __exit__ methods being available. That’s how the block is wrapped.

In Ruby, there’s no language feature like decorators. These are basically function call wrappers, but you define the wrapping when you define the function, instead of at the call site. The code above is similar to contextmanager(open(filename)); that’s the gist of decorators as far as I know.

How do you implement the stuff above in Ruby then?

class MyFile
  def self.opening(filename)
    f = File.open(filename)
    yield f
  ensure
    f.close
  end
end

MyFile.opening("the/filename") do |f|
  f.each_line do |line|
    puts line
  end
end

That’s it, as far as I can tell. The (implicit) block passed to MyFile#opening is invoked with the yield keyword. There’s no built-in functionality apart from this. It’s similar to the Python code – but only on the surface level. Python has interesting things going on behind the scenes in the implementation of the with keyword, and then there’s the code for the @contextmanager decorator itself.

I think the crucial language difference here is that Ruby is designed to make writing block-based methods so easy. By design, Ruby encourages coming up with domain-specific languages (DSLs).

Python’s with keyword reads nicer, but then again you can write this as part of your DSL in Ruby, too, if you support optional blocks in the wrapped method. It does not make sense, but there you go:

class MyFile
  def self.opening(filename)

  end

  def self.opening(filename, &block)
    # Wrap the method body in a lamda so we can either run or return it
    impl = ->(&inner_block) { 
      f = File.open(filename)
      try
        # Explicit version of `yield`
        inner_block.call(f)
      ensure
        f.close
      end
    }

    # Return the lambda for later execution if this is 
    # not a direct call with a block
    return impl if block.nil?

    # If we pass a block, execute right away!
    impl.call(&block)
  end
end

def with(wrapped, &block)
  wrapped.call(&block)
end


MyFile.opening("the/filename") do |f|
  puts "regular:"
  puts f.readlines
end

with MyFile.opening("wrapped/fn") do |f|
  puts "wrapped:"
  puts f.readlines
end

→ Blog Archive