Andy
Andy

Reputation: 1630

Why to avoid forced unwrapping

There are cases where you forgot to set a value (so it's actually a bug), and running the program with forced unwrapping can crash the problem, and that can allow you to track down the bug where you forgot to set the value that you should have set.

From posts talking about avoiding forced unwrapping, it's always brought up that forced unwrapping can crash the program therefore it's a bad thing. What's so bad about crashing a problem when it actually has a bug?

Please give examples where forced unwrapping can be bad.

(I'm not saying forced unwrapping is suitable for everything.)

Upvotes: 6

Views: 2602

Answers (3)

rmaddy
rmaddy

Reputation: 318934

Forced unwrapping (and I'm going to include force-casting as well) should only be used when you, as the programmer, know for a fact that an optional will never actually ever be nil unless that nil represents a clear bug in your code during development (and then you want it to crash).

There are many examples where this type of forced unwrapping is appropriate. Examples include:

  • Getting the path to a known file in your app bundle (nil means you forgot to target the file during development).
  • Force casting a call to UITableView dequeueReusableCell (nil means you have a mistake in your storyboard).
  • Getting a specific component from DateComponents when you just specially asked Calendar for that component (nil mean you have a typo).

There are obviously many other cases where forced-unwrapping is appropriate but you must have a clear understanding of those cases.

But there are just as many runtime decisions that result in optionals that you can't guarantee and such cases should not be forced unwrapped.

Examples include:

  • Dealing with any user input. Never assume a user enters valid data. Never assume a value can be converted as expected. Always check for nil results.
  • Parsing JSON results. Never assume the data you get matches some expected format even if that format is clearly documented and always seems to work. Things change over time. Gracefully handle such unexpected data instead of just assuming a value will always be there and of the assumed data type.
  • Dealing with any API that can throw or return optional results. Things can go wrong. Errors happen. Never assume you will get back a valid answer. Code defensively.

In the end, a developer with the proper experience and understanding of how optionals work, what they mean, and when a value may or may not ever actually be nil is in a position to safely use forced unwrapping when appropriate. Use it wisely.

Never use forced-unwrapping just because Xcode suggested it to make the compiler happy.

Upvotes: 14

pbodsk
pbodsk

Reputation: 6876

There's nothing wrong with crashing a program if it actually has a bug...if you can not work your way around the crash

But as @phillip-mills says in his comment:

...asked by people who use forced unwrapping without understanding that's what's causing the crash

We often see examples where people force unwrap optionals that, for some unexpected reason, isn't there, and in that case it makes sense to try to unwrap the optional "gracefully.

An Example of Bad Forced Unwrapping

Lets say you have a backend service that gives you some JSON which you parse into a model object and present in a view.

Lets say you have a model like this one:

struct Person {
    let firstName: String?
    let lastName: String?
}

And in a UITableView you populate some cells with person objects. Something along the lines of:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    //dequeue your cell
    //get your person element from some array
    //populate
    cell.firstNameLabel.text = person.firstName!
    cell.lastNameLabel.text = person.lastName! 

    return cell
}

At some point you probably will end up with a Person without a firstName (you can say that that is a problem with the backend and so on and so forth but....it'll probably happen :) ) If you force unwrap, your app will crash where it shouldn't be necessary. In this case you could have gotten away with a graceful unwrap

if let firstName = person.firstName {
    cell.firstNameLabel.text = firstName
}

Or you could have used the Nil Coalescing Operator

let firstName = person.firstName ?? ""

Finally, as @rmaddy says in his answer

Forced unwrapping is bad because your program is not guaranteed to be accessing an actual variable at the time of execution

You can not always be sure that the data is what you expect it to be.

And when you have the possibility to actually only do some operation if you're absolutely sure that the data you're operating on is valid, that is, if you've unwrapped it safely...it'd be stupid not to use that option :)

Hope that helps you.

Upvotes: 0

Brandon A
Brandon A

Reputation: 8289

Forced unwrapping is bad because your program is not guaranteed to be accessing an actual variable at the time of execution. When this happens your program might be attempting to perform a mathematical calculation on a number that doesn't exist, and your app would crash. Your point of in the development phase if it crashes you would be able to narrow down why the crash happened and fix the issue of it being nil at runtime for your development phase, but what about in production?

For example, if you were retrieving some sort of number from a web service you may want to compare this number to something local, maybe a version number:

if let json = try? JSONSerialization.jsonObject(with: responseData, options: .allowFragments) as? [String: Any], 
   let serverAPIVersion:NSNumber = json["API_Version_Number"] as? NSNumber {

    if self.currentAPIVersion.uintValue < serverAPIVersion.uintValue {
       self.updateVersionWith(number: serverAPIVersion)
    }

 }

In the code above we are safely unwrapping the "API_Version_Number" from the JSON we get from the server. We are safely unwrapping because if there weren't a value for "API_Version_Number" then when we would try to do the comparison to the current version, the program would crash.

// This will crash if the server does not include "API_Version_Number in json response data
let serverAPIVersion:NSNumber = json["API_Version_Number"] as! NSNumber

And in production there are things out of your control (many times a server side issue) that may lead to unpopulated variables. That is why it is best to conditionally unwrap to gain access to values safely in your code to keep things from crashing at execution time.

Upvotes: 0

Related Questions