Swift 4.2 在单个 if-let 中向下转换多个变量时合并
Swift 4.2 coalescing while downcasting multiple variables in a single if-let
我最近开始学习 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"
,以便在标签中显示。
可能吗?我该怎么做?
你可以试试
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
}
}
}
由于 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([=10=].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"))
})
这有一些主要好处,如下所示,
- 当您将此可选 属性 与
nil
进行比较时,您的 if statement
结果将保持预期。
- 你可以在不同的地方传递不同的默认值而不用任何
if statement
。
- 因为
UITextField
、UITextView
和 UILabel
接受 Optional
文本,在许多情况下,您需要在 [=32] 时显示 placeholder
=] 属性是来自 api 响应的 nil
。因此,在这些情况下,您不必重新设计 string
属性来了解它是否具有默认值或 api 返回值。
我最近开始学习 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"
,以便在标签中显示。
可能吗?我该怎么做?
你可以试试
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
}
}
}
由于 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([=10=].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"))
})
这有一些主要好处,如下所示,
- 当您将此可选 属性 与
nil
进行比较时,您的if statement
结果将保持预期。 - 你可以在不同的地方传递不同的默认值而不用任何
if statement
。 - 因为
UITextField
、UITextView
和UILabel
接受Optional
文本,在许多情况下,您需要在 [=32] 时显示placeholder
=] 属性是来自 api 响应的nil
。因此,在这些情况下,您不必重新设计string
属性来了解它是否具有默认值或 api 返回值。