Home > Net >  Can we use "guard" instead of let <var> = try? <action> in swift?
Can we use "guard" instead of let <var> = try? <action> in swift?

Time:11-20

What is the difference between:

guard let json_data = Data(contentsOf: path) else {return nil}

and

let json_data = try? Data(contentsOf: path)

I dont want to use optional while loading the data into the variable. I want other ways to try it. Thanks in advance.

CodePudding user response:

The options are:

  1. Your first example, unwrapping it with guard, is missing a try?:

    func foo() -> Bar? {
        guard let jsonData = try? Data(contentsOf: path) else { return nil }
    
        // if you get here, `jsonData` is not `Optional`
    
        …
    }
    

    This will safely unwrap your optional and let you do whatever you want if the unwrapping failed. (In your example, you are returning nil.)

  2. Your second example, yields an optional, which you presumably need to unwrap with an if statement.

    func foo() -> Bar? {
        let jsonData = try? Data(contentsOf: path)
    
        // jsonData is `Optional` in this example
    
        if let jsonData {
            …
        } else {
            return nil
        }
    }
    

    We would generally favor the first option over this, where the “early exit” of the guard makes it a little easier to read the code, but there are cases where you might use this pattern.

  3. An option that hasn’t been considered is to actually throw the error (using try instead of try?):

    func foo() throws -> Bar {
        let jsonData = try Data(contentsOf: path)
    
        // `jsonData` is not `Optional`
    
        …
    }
    

    Now, this only passes the buck of handling the error to the caller (i.e., a do-catch block). But it does have a few virtues over the prior two examples, namely that (a) the useful information of the error object is not just discarded, thereby making it easier to diagnose problems during the development process; and (b) you don’t have to return an optional.

  4. Yet another option (to be used only if you know that this will always succeed, e.g., you are reading a well-known file from your bundle that you know must always succeed) is try!, a “force-try”:

    func foo() -> Bar {
        let jsonData = try! Data(contentsOf: path)
    
        // `jsonData` is not `Optional`
    
        …
    }
    

    Now, this will crash if the Data(contentsOf:) can ever fail, so only use this in scenarios where you know that this is impossible.

Personally, I would generally favor option 3 (where I capture what went wrong) in cases where the Data(contentsOf:) might ever plausibly fail at runtime, and I might consider option 4 (where it crashes with a meaningful error message) when I know it is impossible for it to ever fail at runtime. That having been said, more than once I found myself using option 4 and I later regretted not using option 3, simply because there was some weird edge-case that I neglected to consider.

In short, nowadays I tend to defensively catch errors, log the full error in the console and show a nice localized message in the UI (i.e., option 3). I almost never use try?, because if something can fail, it’s rarely a good idea to discard the useful diagnostic information.

CodePudding user response:

It will be better to be optional once you are loading the data to avoid application crash in and problems in case there was no data there. This is consider as a safe feature.

  • Related