Implicitly Crashing Optionals

If you’re an old-time Objective C programmer like me, your first effort to add a Swift file to your project will be met with a cheerful offer from Xcode to “add a bridging header.” This header accommodates the Swift compiler generating Swift interfaces for all the existing Objective C classes you’ll no doubt want to interface with.

At first, I didn’t think too much of this bridging header. Sure, I want to access my Objective C files from Swift. Which ones? Why not all of them?

Life goes on, and as you proceed to write more and more Swift files, you’ll inevitably come to a point where your app crashes because an “implicitly unwrapped optional” turns out to be nil. What happened?

If you’re like me and you haven’t gotten around to annotating all your Objective C headers with nullability tags, Swift imports almost every pointer type as an implicitly unwrapped optional. This means it will be treated as a value that can be nil but is never expected to be nil by the time you access it.

This stuff is all well-covered in Apple’s documentation, but I have never been one to jump straight to RTFM. :)

My takeaway is to accept that it is fundamentally unsafe to interface with Objective C classes whose headers have not been audited for nullability. So back to that bridging header… 

I now impose a rule for my Objective C -> Swift bridging header that any import added to the file must first be confirmed as audited for nullability. If it’s not audited yet, I commence with the somewhat tedious task of annotating (with the help of NS_ASSUME_NONNULL_BEGIN and NS_ASSUME_NONNULL_END, of course) the entire header file, sometimes searching the correlated source files to confirm assumptions about nullability of parameters. When I’m done, I can (relatively) safely import the header into Swift and count on appropriate optional type checking. Except…

In addition to the imported header, of course, I need to audit any headers that the header itself imports. So if there’s some secondary class that works in conjunction with the main class, and whose header is also incorporated into the header, I have to go annotate that class for nullability, too. Here is an opportunity to take stock of whether you really need to import such a class. For example, it might be that a forward declaration will do, and the implementation (.m) file can import the header for internal use only.

Failing to recognize the importance of nullability annotation was probably my biggest mistake yet in my young Swift career. If you’re just getting started tackling Swift from a legacy Objective C source base, here’s hoping you won’t make the same mistake!