A few folks asked this question before, yet no answer was accepted.
I have a UITableViewCell
that contains a UITextField
.
If I click slightly outside of the textField the row highlights. I want to prevent this.
To prevent it I do the following:
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath! {
return nil
}
This works perfectly fine. Here is my question. The author of the tutorial states:
Note that returning nil from a method is only allowed if there is a question mark (or exclamation point) behind the return type.
What does he mean that you can put an exclamation mark behind the optional return type? Why doesn't the program crash? Returning nil after I place an exclamation mark after the IndexPath return type doesn't crash. I thought !
would explicitly unwrap the nil and fail?
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.
It's called implicitly unwrapped since Swift force unwraps it every time. The drawback of this is same as forced unwrapping - if the value is nil when accessing, it leads to a fatal error. Similar to optionals, optional binding and optional chaining can also be used for implicitly unwrapped optionals.
Implicitly unwrapped optionals are a compromise between safety and convenience. If you declare a property as optional, then you need to use optional binding or optional chaining every time you access the value of the optional.
You should therefore ensure that you're not accessing outlets before they're loaded in. You also should check that the connections are correct in your storyboard/xib file, otherwise the values will be nil at runtime, and therefore crash when they are implicitly unwrapped.
As of Swift 3, “Implicitly unwrapped optional” is not a separate type, but an attribute on the declaration of a regular/strong optional. For the details, see SE-0054 Abolish ImplicitlyUnwrappedOptional type.
A function with an IUO return type can return nil
,
and assigning the return value to a variable makes that a regular
optional:
func foo() -> Int! {
return nil
}
let x = foo() // Type of `x` is `Int?`
print(x) // nil
Only if evaluation as an optional is not possible then the value
will be forced-unwrapped (and cause a runtime exception is the
value is nil
):
let y = 1 + foo() // Fatal error: Unexpectedly found nil while unwrapping an Optional value
In your case, your
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath!
method overrides the UITableViewController
method
func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath?
and can return nil
. This does not crash unless the caller unwraps
the value.
Remark: The above is meant as an explanation why your code compiles and works. Generally, I do not see a good reason to use implicitly unwrapped optional return types. The main use-cases of IUOs are stated in SE-0054:
The ImplicitlyUnwrappedOptional ("IUO") type is a valuable tool for importing Objective-C APIs where the nullability of a parameter or return type is unspecified. It also represents a convenient mechanism for working through definite initialization problems in initializers.
One way to think this as a choise of the API Implementor. If implementor handles the input arguments it will not be any problem to the API User.
Lets create a drawing text class which just prints at console.
class TextDrawer {
var mustBeText: String!
func renderText(string: String) {
print(string)
}
func renderSafely() {
renderText(string: self.mustBeText ?? "Nothing found to be rendered")
// or
if let safeString = self.mustBeText {
renderText(string: safeString)
}
}
func renderUnsafely() {
renderText(string: mustBeText)
}
}
We have defined the mustBeText
as String!
means we are allowed to expect any string as well as nil
argument.
Now, as we create a instance of the class as below:
let textDrawer = TextDrawer()
textDrawer.mustBeText = nil // this is allowed since the `mustBeText` is `String!`.
textDrawer.renderSafely() // prints -- Nothing found to be rendered
textDrawer.renderUnsafely() // crashes at runtime.
The renderUnsafaly()
will crash since its not handling the nil
case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With