Search code examples
swiftdefault-valuedowncastcoalescing

Swift 4.2 coalescing while downcasting multiple variables in a single if-let


I recently started learning Swift, now I'm trying to safely unwrap multiple variables that come from a JSON response which might contain or not that key.

Example of the JSON response:

{
    "products: [{
        "foo": "foo"
        "bar": "bar"
    }, {
        "foo": "foo"
    }]
}

Here I'm trying the following:

let dataTask = URLSession.shared.dataTask(with: myURL) { (data, response, error) in
    guard let safeData = data else { return }

    do {
        let json = try JSONSerialization.jsonObject(with: safeData, options: .mutableLeaves)
        if let jsonDict = json as? [String : Any] {
            let productArray = jsonDict["products"] as? [[String : Any]]
            for product in productArray! {
                if let foo = product["foo"] as? String, let bar = product["bar"] as? String {
                    let prod = Product(foo: foo, bar: bar)
                    products.append(prod)
                }
            }
        }
    } catch {
        print ("Error: \(error)")
    }
}

What I want to do is to give bar a default value (coalescing) if the value is nil, such as "Not Available" in order to display it in a label.

Is it possible? How could I do that?


Solution

  • You can try

     let prod = Product(foo:product["foo"] as? String ?? "Not Available" , bar: product["bar"] as? String ?? "Not Available" )
    

    struct Root: Decodable {
        let products: [Product]
    }
    
    struct Product: Decodable {
        let foo: String
        let bar: String?
    
        enum CodingKeys: String, CodingKey {
            case foo , bar  
        }
    
        init(from decoder: Decoder) throws {
    
             let container = try decoder.container(keyedBy: CodingKeys.self)
    
    
            do { 
                let foo = try container.decode(String.self, forKey: .foo)  
                let bar = try container.decodeIfPresent(String.self, forKey: .bar) ?? "Not Available"
                self.init(foo:foo, bar:bar)
    
            } catch let error {
                print(error)
                throw error
            }
         }
    }