使用Swift 4的可解码协议进行嵌套的JSON服务器响应

时间:2017-09-21 15:19:33

标签: swift swift4 codable decodable

我正在尝试实施Swift 4的新Decodable协议,并且很难接近它。

这是我的JSON服务器响应:

{
  "success": true,
  "errorCode": 0,
  "message": "Succcess",
  "data": {
    "name": "Logan Howlett",
    "nickname": "The Wolverine",
    "image": "http://heroapps.co.il/employee-tests/ios/logan.jpg",
    "dateOfBirth": 1880,
    "powers": [
      "Adamantium Bones",
      "Self-Healing",
      "Adamantium Claws"
    ],
    "actorName": "Hugh Jackman",
    "movies": [
      {
        "name": "X-Men Origins: Wolverine",
        "year": 2009
      },
      {
        "name": "The Wolverine",
        "year": 2013
      },
      {
        "name": "X-Men: Days of Future Past",
        "year": 2014
      },
      {
        "name": "Logan",
        "year": 2017
      },
    ]
  }
}

解码响应data部分的最佳方法是什么? 此外,如果data突然变为array而不是对象,会发生什么情况,我如何支持这两种数据类型?

非常感谢:)

1 个答案:

答案 0 :(得分:2)

首先,您可以创建一个扩展名作为帮助者:

extension Data {
    func decode <Generic: Codable> () -> Generic? {
        let decoder = JSONDecoder()
        let object = try? decoder.decode(Generic.self, from: self)
        return object
    }
}


extension Dictionary {
    func decode <Generic: Codable> () -> Generic? {
        let data = try? JSONSerialization.data(withJSONObject: self,
                                               options: JSONSerialization.WritingOptions.prettyPrinted)
        guard let d = data else {
            return nil
        }
        return d.decode()
    }
}

然后,您可以创建协议来帮助您构建对象:

protocol Encode: Codable {
    init(with dictionary: [String: Any])
    init(with data: Data)
}

使用默认实现:

extension Encode {
    init(with data: Data) {
        let object: Self? = data.decode()
        guard let obj = object else {
            fatalError("fail to init object with \(data)")
        }
        self = obj
    }

    init(with dictionary: [String: Any]) {
        let object: Self? = dictionary.decode()
        guard let obj = object else {
            fatalError("fail to init object with \(dictionary)")
        }
        self = obj
    }

然后将对象创建为符合y Codable协议的结构。它看起来像:

struct User: Codable {
    var name: String?
    var nickname: String?
    ...
    // If needed declare CodingKey here
    // enum CodingKeys: String, CodingKey {
    //     case date = "dateOfBirth"
    //     ...
    // }
}

struct Movies: Codable {
    var name: String?
    var year: Int?
}

现在,您需要从响应中提取数据字典并应用新的init方法:

if let dic = json["data"] as? [String: Any] {
    let user: User = User(with: dic)
    // Do stuff here
}

如果数据突然成为一个数组,则必须以不同的方式处理它(在此示例中为用户数组)