What is difference between optional and decodeIfPresent when using Decodable for JSON Parsing?

前端 未结 3 1583
执念已碎
执念已碎 2020-12-02 17:09

I am using Codable protocol from Swift 4 first time, I am not able to understand use of decodeIfPresent from Decodable.



        
3条回答
  •  攒了一身酷
    2020-12-02 17:54

    I think it makes sense to use decodeifPresent rather than an optional property if you want to use a default value for a property that could be missing from the JSON.

    For example, let's examine 3 situations:

    1. All the keys are present in the JSON:

    Let's suppose you must decode this JSON:

    {
        "project_names": ["project1", "project2", "project3"],
        "is_pro": true
    }
    

    You can use this struct:

    struct Program: Codable {
        let projectNames: [String]
        let isPro: Bool
    }
    

    and you will get a Program object with a isPro value equal to true. (I suppose your decoder keyDecodingStrategy is .convertFromSnakeCase in the rest of this example)


    2. Some keys are missing in the JSON and you're ok to have an optional in Swift:

    {
        "project_names": ["project1", "project2", "project3"]
    }
    

    You can now use this struct:

    struct Program: Codable {
        let projectNames: [String]
        var isPro: Bool?
    }
    

    and you will get a Program object with a isPro value equal to nil.

    If the JSON looked like this:

    {
        "project_names": ["project1", "project2", "project3"],
        "is_pro": true
    }
    

    then isPro would be a Bool? with value true. Maybe that's what you want, but probably you would like to have a Bool with a default value of false. That's where decodeIfPresent could be useful.


    3. Some keys are missing in the JSON and you want a non-optional property with a default value in Swift:

    If your struct looks like this:

    struct Program: Codable {
        let projectNames: [String]
        var isPro: Bool = false
    }
    

    then you will get a parsing error if the "is_pro" attribute is not present in your JSON. Because Codable expects to find a value to parse a Bool property.

    In that situation, a good idea would be to have an initializer with decodeIfPresent, like so:

    struct Program: Codable {
        let projectNames: [String]
        let isPro: Bool
    
        init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            self.projectNames = try container.decode([String].self, forKey: .projectNames)
            self.isPro = try container.decodeIfPresent(Bool.self, forKey: .isPro) ?? false
        }
    }
    

    This allows you to have the best of both worlds:

    • your struct has a Bool, not a Bool? property
    • you are still able to parse a JSON that does NOT contain the "is_pro" field
    • you can get a default value of false if the field is not present in the JSON.

提交回复
热议问题