Swift 4.2 объединяется при понижении нескольких переменных в одном if-let - PullRequest
0 голосов
/ 27 декабря 2018

Я недавно начал изучать Swift, теперь я пытаюсь безопасно развернуть несколько переменных, которые приходят из ответа JSON, которые могут содержать или не содержать этот ключ.

Пример ответа JSON:

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

Здесь я пытаюсь сделать следующее:

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)")
    }
}

Я хочу дать bar значение по умолчанию (объединение), если значение равно nil, например "Not Available" inДля того, чтобы отобразить его в метке.

Возможно ли это?Как я мог это сделать?

Ответы [ 2 ]

0 голосов
/ 27 декабря 2018

Поскольку ключи в json являются Decodable дружественными, вы можете использовать этот минимальный код,

struct ProductResponse: Decodable {
    let products: [Product]
}

struct Product: Decodable {
    let foo: String
    let bar: String?
}

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

    do {
        let productResponse = JSONDecoder().decode(ProductResponse.self, from: data)
        print(productResponse.products.forEach({ print($0.foo)}))
    } catch {
        print ("Error: \(error)")
    }
}

Назначение значения по умолчанию для bar на уровне синтаксического анализа не кажется естественным.Product - это простой тип с двумя свойствами, но для типа с десятками свойств вам не понравится реализация init(from decoder: Decoder) и CodingKeys enumerations только из-за одного или двух свойств, которым необходимо значение по умолчанию.

Я бы предложил лучший подход, введя от extension до Optional, как показано ниже,

extension Optional where Wrapped == String {

    /// Unwrapped string
    /// `u7d` comes from starting `u` in `Unwrapped`, 
    ///  7 letters in between and last letter `d`.
    public func u7d(_ defaultString: String = "N/A") -> String {
        guard let value = self, value.isEmpty == false else { return defaultString }
        return value
    }
}

Так что теперь, когда вы хотите использовать значение по умолчанию value, если это свойство nil, вы можете просто развернуть его, передав значение по умолчанию value, как показано ниже,

productResponse.products.forEach({ product in
    print(product.bar.u7d("Not Available"))
})

Это имеет некоторые ключевые преимущества, как показано ниже,

  • Ваш результат if statement останется ожидаемымкогда вы сравните это необязательное свойство с nil.
  • Вы можете передавать различные значения по умолчанию в разных местах без if statement.
  • как UITextField, UITextView и UILabel примите Optional текст, и во многих случаях вам нужно будет показать placeholder, когда атрибут string равен nil из ответа API.Таким образом, в этих случаях вам не нужно перепроектировать атрибут string, чтобы узнать, имеет ли он значение по умолчанию или значение, возвращаемое API.
0 голосов
/ 27 декабря 2018

Можно попробовать

 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
        }
     }
}
...