Swift's nonmutating Keyword

In preparing my recent “Hidden Gems in Swift” talk, I carefully combed through Swift’s “Lexical Structure” section. One keyword that stuck out to me, but ultimately didn’t make it into the talk was nonmutating. Much like the @nonobjc attribute, it surprised me that I hadn’t seen this keyword in the wild.

In searching for example uses, I stumbled upon this conversation between Andy Matuschak and Sidney San Martín:

Stepping through Sidney’s example, we can see how nonmutating signals that a setter doesn’t modify the containing instance, but instead has global side effects.

Whether or not this is good practice is a larger question. But, it’s definitely worth adding to your tool belt!

Hiding Selector Methods

On the Tumblr iOS team, we have a strict rule that everything with an internal or public visibility must be documented1. This gets a bit awkward when using the Target-Action pattern that UIKit often forces you into. When dealing with multiple action selectors, it makes sense to factor out the target into a separate object. However, we’ll often just need a one-off action selector to handle an event from a UIControl subclass. Previously, this would lead to the following scenario and awkward comment:

I was curious if we could make SampleViewController.buttonTapped(_:) private, thus avoiding the need to specify that the method shouldn’t be called externally, in its documentation. My teammate Paul pointed out that this is actually possible! To do this, we simply have to expose the method to the Objective-C runtime and give it a private access modifier:

This allows our action to be invoked from a selector, but prevents other files from accessing the method directly 🎉 Hope this helps better encapsulate your types!

1: To help with this, we use VVDocumenter.

TIL About @nonobjc

In my free time, I run a Twitter account called Public Extension, where I share handy Swift extensions I’ve found or come up with. A recent extension involved adding custom subscripts to NSUserDefaults.

One attribute that sticks out is @nonobjc. Often times, you’ll need to use @objc1 to expose your Swift classes that don’t derive from an Objective-C class to the Objective-C runtime. It’s rare to explicitly cut that interoperability. Let’s dig into why this attribute is necessitated here by dropping it and deconstructing the compilation error:

The full error reads “Subscript getter with Objective-C selector ‘objectForKeyedSubscript:’ conflicts with previous declaration with the same Objective-C selector” (and the analog for the setter). Turns out that Objective-C also supports custom subscripting and Swift will automatically bridge them back! However, in our example, we’re overloading the subscript with the same key and different return types (also referred to as return type polymorphism), which Objective-C doesn’t support 😔. To fix this, we simply prevent the default bridging via @nonobjc.

So, there you have it! A real example of the @nonobjc attribute in action. Hope this post sheds some light on why it exists 😃

Shout-out to Joe Groff for the help and Garric Nahapetian for motivating me to write this post!

1: Swift also allows for the @objc(name) variant, which uses name when exposing the target class to the Objective-C runtime.