Come to the Dark Side — WAYTheDarkSide

I remember it clearly when we were at the WWDC ’14 and Craig Federighi on stage announced that OS X Yosemite will feature a brand new Dark Mode — the cheering crowd responded.

Still, several weeks after OS X Yosemite has been released, there aren’t that many Mac applications out there that truly take into account the new global preference which influences the look of the whole system quite substantially. In some situations, it may be appropriate to adapt the dark look in your application.

For example if you have large, bright areas in your UI, you should consider customizing them for a dark appearance, so that your app is still soft to your users’ eyes late at night.

One of the reasons why developers haven’t made use of the new OS X feature might be that determining if the user has switched to the Dark Mode is not as easy as it could be. There are no obvious APIs to ask for the current setting and be notified about changes.

Today we’re releasing WAYTheDarkSide, which utterly simplifies the adaption of the Dark Mode in Yosemite. This is how it works:

Distributed Notifications & the Global Persistent Domain

Let’s look at two things to properly handle the Dark Mode:

First of all, to detect the current state while starting your application, there are multiple ways to go. One of them involves running an Apple Script that reads from the system settings. However we are using a clean, pure Cocoa based approach: reading the NSGlobalDomain persistent domain and asking for the BOOL value of the AppleInterfaceStyle key.

Next, to observe the state continiously while running, we need to listen to the Distributed Notification with the name AppleInterfaceThemeChangedNotification. This notification will be fired by the system whenever the user toggles the Dark Mode on or off. NSDistributedNotificationCenter is your friend.

Handling the Dark Mode with WAYTheDarkSide

We have wrapped the two techniques into a class, which provides multiple interfaces, that make handling the Dark Mode as easy as pie.

In the simplest case, you simply define a block which will be performed when the Dark Mode is switched on, and one for when the Dark Mode is switched off:

// The application decided to join the dark side
[WAYTheDarkSide welcomeApplicationWithBlock:^{
    [weakSelf.window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]];
    [weakSelf.contentView setMaterial:NSVisualEffectMaterialDark];
} immediately:YES];
// The application decided to leave the dark side
[WAYTheDarkSide outcastApplicationWithBlock:^{
    [weakSelf.window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantLight]];
    [weakSelf.contentView setMaterial:NSVisualEffectMaterialLight];
} immediately:YES];

Well, we had fun finding the naming here. The two blocks will not only be triggered upon a preference change event, but also while starting the app, since we have set immediately:YES. See the headers for details.

Since you hopefully have architectured your application with several NSViewControllers and NSWindowControllers, you will however probaly rather make use of the per-object based API. This will let you define handler blocks individually for each controller, that owns any UI which should reflect Dark Mode changes:

[WAYTheDarkSide welcomeObject:self withBlock:^{
     /* ... */ 
} immediately:YES];
[WAYTheDarkSide outcastObject:self withBlock:^{ /* ... */ } immediately:YES];

That’s it. Note that the class does not provide any automic NSAppearance or NSVisualEffectMaterial handling. It is up to you how you define your application’s look in the respective states.

The class also provides some helper methods, such as applicationReadyForTheDarkSide; and applicationIsOnTheDarkSide;.

Find the repository on GitHub.

PS: Have a look at [NSImage setTemplate:]; concerning your NSStatusItem images!


If you liked this posting, you might want to join our newsletter. We only send out newsletters if we have something important to say. No spam, promise.

Questions? @raffael_me or @weareYeah.