Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to check non-optional values for nil in Swift

In Swift, it's rare but possible to end up with a value of a non-optional type that has a nil value. As explained in answers to this question, this can be caused by bad Objective-C code bridged to Swift:

- (NSObject * _Nonnull)someObject {
    return nil;
}

Or by bad Swift code:

class C {}
let x: C? = nil
let y: C = unsafeBitCast(x, to: C.self)

In practice, I've run into this with the MFMailComposeViewController API in MessageUI. The following creates a non-optional MFMailComposeViewController, but if the user has not set up an email account in Mail, the following code crashes with EXC_BAD_ACCESS:

let mailComposeViewController = MFMailComposeViewController()
print("\(mailComposeViewController)")

The debugger shows the value of mailComposeViewController like this:

mailComposeViewController = (MFMailComposeViewController) 0x0000000000000000

I have a couple of questions here:

  1. I note that the documentation for unsafeBitCast(_:to:) says it "breaks the guarantees of the Swift type system," but is there a place in Swift documentation that explains that these guarantees can be broken, and how/when?
  2. What's the best, most idiomatic way to check for this case? The compiler won't let me check whether mailComposeViewController == nil since it's not optional.
like image 468
Luke Avatar asked Oct 30 '17 15:10

Luke


People also ask

How do you check if something is nil Swift?

In Swift, if we define a variable to be an optional variable, in that case, this variable can have no value at all. If optional variable is assigned with nil , then this says that there is no value in this variable. To check if this variable is not nil, we can use Swift Inequality Operator != .

Is nil a value in Swift?

In Swift, nil means the absence of a value. Sending a message to nil results in a fatal error. An optional encapsulates this concept. An optional either has a value or it doesn't.

How do you check if a variable has a value in Swift?

You can use if let. if let is a special structure in Swift that allows you to check if an Optional holds a value, and in case it does – do something with the unwrapped value. But for Strings you can also use . isEmpty() If you have initialized it to "" .

What will happen if you try to unwrap an optional that contains nil like so?

Unwrap an optional type with the nil coalescing operator If a nil value is found when an optional value is unwrapped, an additional default value is supplied which will be used instead. You can also write default values in terms of objects.


1 Answers

Even Apple's APIs sometimes return nil for a type that is not marked in the API as Optional. The solution is to assign to an Optional.

For example, for a while traitCollectionDidChange returned a UITraitCollection even though it could in fact be nil. You couldn't check it for nil because Swift won't let you check a non-Optional for nil.

The workaround was to assign the returned value immediately to a UITraitCollection? and check that for nil. That sort of thing should work for whatever your use case is as well (though your mail example is not a use case, because you're doing it wrong from the get-go).

like image 145
matt Avatar answered Sep 28 '22 22:09

matt