Category Archives: Swift

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!

Brent’s Swift Tension

Brent Simmons has been writing new code exclusively in Swift for a while now, and he recognizes omissions from the language that he still anticipates will be hard to overcome as Apple presumably moves toward incorporating Swift into its developer-facing frameworks. The Tension of Swift:

The Objective-C runtime allows AppKit and UIKit to provide some powerful features that I’d hate to do without: the Responder Chain, xib and storyboard loading, KVC, and the undo manager.

A key point he gets at is that however great Swift is, it’s only possible to use it to develop functional iOS and Mac apps because of the Objective C runtime still operating behind the scenes. Both AppKit and UIKit not only lean on the functionality of the runtime, but are designed with the runtime in mind.

Some major design priorities of the Swift language, namely type safety and compile time dependency binding, are at odds with the design priorities of 20 years of evolution in Apple’s frameworks. How and if that disparity will be reckoned by Apple remains to be seen.

I’m optimistic, because the Swift team has already made many concessions to make the language more compatible with the Objective C runtime. It strikes me as possibly non-optimal that a language that strikes the right compromise between Swift’s priorities and Objective C’s would start at the opposite extreme and work its way backwards, but that is what Apple seems to be doing.

Let’s hope they continue in that direction, and surprise us all with how well it all works out in the end.

Scripted Swift Warnings

Since I complained yesterday about Swift’s lack of a counterpart to Objective C’s #warning directive, folks have been in touch to encourage another solution to this problem. I could take advantage of Xcode’s standardized, built-in support for text-based comments such as “// FIXME” and “// TODO”. Using this notation causes the affected source lines to show up in Xcode’s per-file function popup, and other folks affected by the issue I describe above have added scripted build phases to emit text that Xcode will treat as a warning for each of these lines.

I initially dismissed the solution out of hand, because I anticipated that searching all of my sources with every build would be excessive. I’m kind of a stickler for fast build times because I like to do quick iterations when writing and testing code. My largest source base, MarsEdit, is big but not huge. But, appreciating that computers and SSDs are fast, I decided to test the straightforward script linked above to see how much time it would add to each of my build iterations. On average, it’s about 1.2s per build. It doesn’t sound like much, but personally that would nag at me. I’ve made a fuss over build phase speeds in the past, and reported bugs against unnecessary slowdowns in Apple’s standard build phases.

Zev Eisenberg suggested on Twitter that the use of specialized search tools such as the_silver_searcher might help. It’s true, it might, but then I’d be going down a path of installing custom software, writing custom scripts, all to accommodate what is a very useful feature that I personally still believe should be built in to the language. Or, at the very least, the language’s feature set should accommodate efficiently extending it to support something like #warning.

We’ll see where things go. I may yet end up adopting a convoluted (to my mind) approach involving expensive, redundant build phase script that scans my entire source base with every build, but more likely I’ll keep scratching my head while using the one-line, inline hack that I previously described.

Artificial Swift Warnings

I’ve been tackling more and more of my coding challenges in Swift, recently. I’ve run into a number of hangups. Some attributable to the learning curve, some to bugs in Swift or Xcode, and some to features I’ve grown to love in clang and Objective C which are simply not present in Swift.

For years, I’ve been in the habit of tagging my in-progress code with “artificial warnings.” While working in code, if a concern crosses my mind, the easiest way to make sure I won’t ship the software without addressing that concern is to add it directly to the code:

#warning Step through this in a debugger and confirm it still works...

Or:

#warning This isn't implemented yet, need to handle XYZ...

These warnings serve as an active reminder of things to fix while I’m working in Xcode, since they show up in the build navigator, and are illuminated in the source code while stepping through with the debugger. And because of my strict “no warnings” policy for shipping code, they are guaranteed to show up as errors in any Release build, thus thwarting an accidental shipment of code that is known to need further refinement.

I don’t think Swift supports anything like the “#warning” preprocessor directive.

The closest I’ve come to matching this behavior is a trick that employs Swift’s willingness to emit a warning for unreachable code:

if false { "in lieu of #warning, this will do" }

Unfortunately, it only shows up in the issue navigator as “Will never be executed,” and doesn’t show the specific warning text contained in the string. But at least as soon as click on the warning, I am reminded of the concern at hand.

I don’t know if the Swift team is philosophically opposed to implementing support for #warning, or something like it. I filed an enhancement request with the Swift project. In the meantime, if false { “life goes on.” }.

Update: Many folks have suggested a build-phase script to tag warnings. I wrote more about that option in a followup post.

A Tale Of Two Optimizers

I have quipped a few times that my biggest problems with Swift so far have to do with struggles in the debugger. It seems slow, inaccurate, harder to use than in Objective C. Some of this is just a learning curve, but other aspects seemed fundamentally broken. I whined on Twitter about a scenario in which lldb seemed utterly unaware of one of my variables:

A kind Apple employee, Kate Stone, followed up with me and ultimately encouraged me to file a bug:

I obliged, filing Radar #26032843. Today, Apple got back to me with a followup, suggesting rather gently that I may have neglected to disable optimization in my target. Rookie move! The kind of behavior I was seeing in the debugger is exactly what happens when lldb can’t make as much sense of your code because of inlined functions, loops that have been restructured, etc.

In fact, I had correlated the symptoms with such a problem, but when I went to check on the status of my optimization settings, everything looked fine. Why? Because I was looking, by habit, at the Clang LLVM “Code Generation” settings for optimization:

MarsEdit xcodeproj

See? Optimization disabled. Just as it is for all my projects, and all my targets, because I define it once in my centralized Debug “.xcconfig” file, to be sure I never screw it up:

// We only specify an optimization setting for Debug builds.
// We rely upon Apple's default settings to produce reasonable
// choices for Release builds
GCC_OPTIMIZATION_LEVEL = 0

So why does debugging Swift fail so hard for me? Because Swift doesn’t use that optimization setting. Scrolling down a little farther, I find the culprit in Swift’s own compiler settings section:

MarsEdit xcodeproj

So the lesson is that new Swift developers coming from a legacy of Objective C, C++, or C development need to take stock of Swift compiler settings because they are liable to be rooted in completely different build settings. On the one hand, I’m glad Apple is finally able to get away from a build setting like “GCC_OPTIMIZATION_LEVEL” (though keeping the name in the GCC -> LLVM transition prevented problems like this back then), but on the other hand, it’s kind of annoying to have to express high level directives that affect whether my code will be debuggable or not using multiple build settings.

At least, because I am not an animal, this will also only ever need to be done once, with an edit to the pertinent “.xcconfig” file:

// We only specify an optimization setting for Debug builds, we rely upon
// Apple's default settings to produce reasonable choices for Release builds
GCC_OPTIMIZATION_LEVEL = 0
SWIFT_OPTIMIZATION_LEVEL = -Onone

Now if you’ll excuse me I’m going to take a tour of other Swift-specific compiler settings to make sure I’m not shooting myself in the foot in some other way!

Test With Swift

I have recently passed a sort of tipping point where I’m indulging more and more in Swift for new code that I add to my projects. There are some instances where I will still create a new class in Objective C, primarily where I anticipate the need for dynamic runtime hijinx that might be more complicated in Swift. In general though, I’m opting for Swift. Finally.

There are many reasons to remain gun-shy about Swift, and I don’t fault anybody too much for choosing to continue forestalling the transition. I’ve spoken with many people who are as tentative as I was or moreso. Some of our collective reasons for waiting may sound familiar to you:

Swift …

  • … is not mature.
  • … requires adding bloated libraries to the app.
  • … presents an impedance mismatch with existing Cocoa design patterns.
  • … is still too risky for production code.

I don’t agree with all of these rationale, especially now that I’ve decided to dive in myself. However, they make a good basis for the argument I’d like you to consider: you should write all new unit tests in Swift.

For many of us who spent years developing a vast collection of Objective-C based classes, it does seem daunting to transition to a new language. But unit tests are different from “regular code” in a number of ways that make them a suitable place to start delving into Swift:

Unit tests …

  • Don’t ship to customers.
  • Can be as bloated as you like.
  • Test the exposed interfaces of classes more than the internal design.
  • Are not technically production code.

I’m sure somebody will argue that tests are so vital to the development process, that they are the last place one should invest in risky technology. I guess what I’m urging you to believe is that Swift is no longer risky technology. It’s not longer coming, it’s here. We will serve ourselves well to adopt it as quickly as practical. And those of us who are daunted by the challenge incorporating it into our existing, Objective-C heavy source bases, have a perfect opportunity in unit testing to get our feet wet while establishing a Swift source base that will live on well into the future. After all, your unit tests should, in theory, outlive any specific implementations of your shipping code.

The Seven Stages of Swift

For all except the newest members of our Apple development community, the arrival of Swift is something that has to be reconciled against our previous and ongoing relationship with Objective-C. This handy reference will help to guide you through this journey. Where do you fall in the spectrum? How you will advance to the final stage?

Shock and Disbelief

Apple announced a new programming language? They didn’t. They couldn’t. This is not WWDC. That’s not Tim Cook. The world is upside down! Why is John Siracusa smiling?

Denial

Swift is not even a real programming language. I mean, it’s not replacing Objective-C. I mean, It’s not meant for real Cocoa programmers. I mean, it’s just Apple trying to satisfy those … weird programmers who like buzzwords! Objective-C is here to stay.

Anger

Oh crap, Swift is replacing Objective-C! Arrrrrrrggghhh! Why, Apple!? WTF? Where’s Objective-C 3.0? You’re killing Objective-C?! I love Objective-C. Die in a garbage collected fire!!!

Bargaining

I suppose Swift might be useful someday. Support for dynamic message dispatch is a start. If Apple gives me just one more Objective-C feature, I’ll give it a shot.

Guilt

Ugh. I should really start learning about Swift one of these days! What is wrong with me?

Depression

Who cares? I could learn Swift, but what’s the point? My Objective-C skills are useless. Dot notation won. I feel this inexplicable weight in my arms. Static types are hanging from my arms. I can’t move my arms. Our apps are all going to die someday.

Acceptance and Hope

Holy crap, Swift made this so easy! Maybe I like Swift? I mean, I still love Objective-C, but I don’t love love it. Maybe we needed a new language. Maybe we were aching for a new language. Maybe we needed more buzzwords.

I like buzzwords.

(Why is John Siracusa smiling?)