Is it bad practice to use force/implicit unwrapping, "as!" and "try!" syntaxes?

Often see people stating that it is since they are "unsafe" and may cause a runtime crash. These language elements look handy for defining invariants without boilerplate, and crash log (more so Xcode debugger) seems to be informative enough to determine the reason of the crash. Do I miss something here?

If you know that it's not possible for it to crash, based on the log at that point in your program, then it's not bad to do. Generally it's safer though to do something like:


if let thing = thing {
   thing.someFunc()
}


In that simple case obviously you could just use a ? instead, but if you're doing multiple operations, just unwrap it.

What that safety means? We should keep our programs not crashing instead of relying on invariants? What if that "thing" in your example has become nil by accident? The program will just skip the bug and its behaviour will become indeterminate. Do Swift really designed to do things in that way? It lacks sense...

Accepted Answer

If the only thing that could cause a nil value is a programming error, then great, use the force unwrap. As you say, it's a very concise way to enforce an invariant.


But if you're talking about some system API that returns an optional, which could be nil at runtime for a variety of reasons, then you must not blindly force unwrap. You have to think about the consequences if it could be nil.


So whether it's a good idea or not varies case by case.

I definitely prefer your code snippet to the '?' operator because I can write an "else" after it and decide what to do rather than trusting that ignoring a problem is a good idea.


I decided '?' was dangerous when I recently saw someone write code like this without any compiler error.

import UIKit
import AVFoundation
class Reporter {
  var player: AVPlayer = AVPlayer()
  var musicPlayer: AVPlayer?

  func test() {
    if musicPlayer == player.currentItem?.canPlayReverse {  // Really?!
      print("Interesting comparison")
    }
  }
}

What that safety means?

That’s a question that’s as old as assertions themselves. Like you, I tend to err on the side of trapping so that I uncover problems during development, but I understand that opinions differ.

One thing I strongly advocate is that you try to minimise the number of unwraps. For example, consider this code:

var task: NSURLSessionTask?

func start() {
    self.task = NSURLSession.sharedSession().dataTaskWithURL(…)
    self.task!.taskDescription = …
    self.task!.resume()
}

where

task
is nil if there’s no task running. In that case I prefer to write
start()
as:
func start() {
    let task = NSURLSession.sharedSession().dataTaskWithURL(…)
    task.taskDescription = …
    task.resume()
    self.task = task
}

That way, someone reading the code doesn’t have to look at exclamation marks scattered throughout the function.

I’ve found that

if let
,
guard let
and
let xxx = yyy!
are all useful constructs for this.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I gathered some typical cases with respect to iOS development:

struct SomeType {}
class AViewController: UIViewController {
     // CASE implicitly unwrapped optional
    var someProperty: SomeType!

    @IBOutlet weak var textField: UITextField!

    /*

     code...
     here we assign a value to someProperty

     */

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        // CASE force unwrapping
        switch segue.identifier! {
        case "showB":
            let destination = segue.destinationViewController as! BViewController // CASE force casting
            destination.someProperty = someProperty
            destination.textProperty = textField.text! // CASE force unwrapping
        default:
            fatalError("Unknown segue identifier")

        }
    }

}
class BViewController: UIViewController {
    // CASE implicitly unwrapped optional
    var someProperty: SomeType!
    var textProperty: String!
    // CASE plain optional
    var plainOptional: SomeType?


    /*
     code...
     someProperty and textProperty are used in multiple points of this class and are not expected to be nils anywhere
     plainOptional used once with safe unwrapping: if it is nil nothing will happen, if not this view controller should show something

     */
}
// some class from Foundation module
struct NSSome {
    init?(value: String) {
        // code... //
    }
}
class SomeModelClassA {
    var property: NSSome


    // CASE force unwrapping
    // we don't expect provided value to be invalid
    init(value: String) {
        property = NSSome(value: value)! /
    }
}
class SomeModelClassB {
    var property: NSSome

    // CASE safe unwrapping
    // we expect that provided value could be invalid
    init?(value: String) {
        if let x = NSSome(value: value) {
            property = x
        } else {
            return nil
        }
    }
}
class SomeModelClassC {
    var property: NSSome

    struct Error: ErrorType {
        let message: String

        init(message: String) {
            self.message = message
        }
    }

   // CASE safe unwrapping
   // we expect that provided value could be invalid and we need to attach an important message to explain why it is invalid
    init(value: String) throws {
        if let x = NSSome(value: value) {
            property = x
        } else {
            throw Error(message: "important message")
        }
    }
}
struct Predicate {}
class Database {
    func getUrl(predicate: Predicate) -> String {
        return "url string"
    }
}
class SomeModelClassD {
    enum Error: ErrorType {
        case BadImageData
    }

    let database: Database

    init() {
        database = Database()
    }

    func getImage(predicate: Predicate) throws -> UIImage {
        let urlString: String = database.getUrl(predicate)
        // CASE force unwrapping (I validated URL strings before adding records to database or that strings have come from a reliable source)
        let url = NSURL(string: urlString)!

         // CASE safe try (probabilty of throw is low but still sufficient (e.g. connection problem) for handling requirement)
        let data = try NSData(contentsOfURL: url, options: .UncachedRead)

        // CASE force unwrapping (I think that probability of incorrect image data passing is too low and therefore don't handle it)
        return UIImage(data: data)!
    }
}


 

and would like to know if they are ok or there are some better options.

So there is no strong directive from Apple about this syntax usage and I am free to use it if find it to be appropriate (e.g. like in cases in the post below)?

More opinions. 🙂


To me it all boils down to the definition of "not expected to be nil". If the ONLY way it could be nil / invalid is a programming error (wrong segue name, connected to wrong type of VC, outlet not connected, forgot to initialize property in an initializer, fatal OS issue such as malloc failed etc.) then IUO is great. But if there is any way at all that the invalid value could happen at runtime due to user input, network failure, unexpected response from a server, transient or recoverable errors of any kind, then it is better not to crash.


Some cases are not black and white. Such as your last case of loading stuff from a database. If you validated the data yourself before you stored it, then maybe you could argue that it should be OK when it comes out. Myself, I don't trust opaque third party code all that much so I would check for errors in that case rather than using a force unwrap assuming your UIImage was created successfully, which would crash your app if the database became corrupted somehow.

So there is no strong directive from Apple about this syntax usage …

Apple does not provide any “strong directives” at this level. Ultimately you’re the one who has to write and maintain your code; you should write it in the way that best meets your needs.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thank you for the answers

Is it bad practice to use force/implicit unwrapping, "as!" and "try!" syntaxes?
 
 
Q