At work last week, I was debugging some odd behavior in one of our apps. A user,
on a detail screen, would tap a Save
button, and nothing would happen. Not a
crash, not an error message, no log entries. Just, nothing.
I started digging into the code, and quickly found we were trying to add this new item represented by the detail screen onto a custom collection. Something along these lines (the names have been changed to protect the guilty):
[self.customCollection addItem:self.collectionItem];
As it turns out, a couple of classes above this one, the instance of customCollection
wasn’t being properly initialized; it was simply nil
.
That means in the code above, we were passing a message to nil
. In Objective-C,
that means the message is dropped on the floor, and ignored.
In many other languages, such as C♯, calling a method on a null
variable
would cause a crash. Messaging to nil
being acceptable is considered a
feature in Objective-C, and not a bug. Crashes are never a good thing,
after all.
In Swift, by comparison, we can only have nil
values on Optional
s.
We do have the handy optional chaining syntax, however, that would change
the code above to:
self.customCollection?.add(item: self.collectionItem)
As a Swift developer, seeing that ?
would immediately cue to me that it’s
possible for customCollection
to be nil
. Something that is worth investigating.
That being said, any Objective-C developer worth their salt will think the same
of any class instance. To a Swift developer, that sounds exhausting, but it’s
still a reasonable point. Further, many Swift developers only use optional
chaining in cases they’re pretty damn sure things will either be non-nil
, or
it’ll work itself out if things are nil
. The ?
may get overlooked.
In our case, where nil
really is bad news, a good Swift developer would improve
upon the above:
guard let collection = self.customCollection else {
// Take evasive action, or just punt:
fatalError("Custom Collection cannot be null!")
}
// If we get here, we are *guaranteed*
// that collection is non-nil.
collection.add(self.collectionItem)
Here, it’s made explicitly obvious what’s happening: the guard let
indicates
this thing had better be true, or else bad things have happened.
“But wait!” shouts the Objective-C developer. “We have NSAssert
!”
Okay, sure. But NSAssert
isn’t always enabled; in fact, in general, it’s
disabled by most in release builds.
The point isn’t that Swift’s Optional
s or guard let
have no equivalents
in Objective-C. The point is that thinking deliberately about these
things—and proactively protecting yourself from error conditions—is
a fundamental part of how you write Swift.
Some people may find being that concerned with what is Optional
and what isn’t
to be exhausting. I find it to be lovely. I find myself being far more
deliberate with the Swift code that I write. In many ways, it’s reminiscent of
the C♯ I wrote in the past, but with better tools such as guard let
and
optional chaining (which was just added in C♯ 6.0, actually).
I like Swift, and I like being that careful with my code. Swift instills a care in me that I like to think is innate, but is now compulsory.