对应的类型可能多种,如果 Decode?

Codable是Swift史上最赞的功能之一。但是项目中我们可能会遇到下面的这种情况:服务器返回的json中同一个字段可能会解析成不同的类型。

{
    "contents": {
        "icon": "icon-name",
        "font": "font-name"
    }
}

{
    "contents": "text"
}

contents可能是icon font,也可能是普通的String。这种情况我们如果用Decodable来处理呢?

方法1: Either

contents可能是icon font或String。或的关系我们可以定义一个枚举Either来表示。

enum Either<T, U> {
    case left(T)
    case right(U)
}

我们可以利用Swift的conditional conformation特性,当T和U都是Decodable类型时候,Either也是Decodable。

extension Either: Decodable where T: Decodable, U: Decodable {
    init(from decoder: Decoder) throws {
        if let l = try? T(from: decoder) {
            self = .left(l)
        }
        else if let r = try? U(from: decoder) {
            self = .right(r)
        }
        else {
            let context = DecodingError.Context(
                codingPath: decoder.codingPath,
                debugDescription: "Cannot decode \(T.self) or \(U.self)"
            )
            throw DecodingError.dataCorrupted(context)
        }
    }
}

实现很简单,首先尝试解析成T,如果不成功的话再尝试解析成U。如果都不成功则抛出异常。

利用Either,我们就可以处理之前同一个字段可能解析成不同类型的问题了。
```swift
let iconJson = """
{
"contents": {
"icon": "icon-name",
"font": "font-name"
}
}
""".data(using: .utf8)!

let textJson = """
{
"contents": "text"
}
""".data(using: .utf8)!

struct IconFont: Decodable {
let icon: String
let font: String

top Created with Sketch.