3

我正在尝试实现 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而不是一个对象会发生什么,我怎样才能支持这两种数据类型?

非常感谢 :)

4

1 回答 1

3

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

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
    }

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

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
}

如果数据突然变成一个数组,你将不得不以不同的方式处理它(在这个例子中作为一个用户数组)

于 2017-09-21T15:58:25.680 回答