Использование протокола Decodable Swift 4 для вложенного ответа сервера JSON

Я пытаюсь реализовать новый протокол Decodable в Swift 4, и мне трудно к нему приблизиться.

Это мой ответ сервера 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 вместо объекта, как я могу поддерживать оба типа данных?

Большое спасибо :)


person Erez Hod    schedule 21.09.2017    source источник
comment
Also, what happens if the data is suddenly an array instead of an object, how can I support both data types? - использовать enums со связанными значениями в вашем типе? Один случай с array, другой с dictionary.   -  person user28434'mstep    schedule 21.09.2017
comment
Чтобы узнать, как для написания типов, отражающих ваше представление JSON. В основном это включает в себя написание типа и соответствие Codable, а остальное должно быть сделано за вас.   -  person Itai Ferber    schedule 21.09.2017
comment
Для тех, кто следит. Еще одно хорошее чтение (medium.com/swiftly-swift/)   -  person Tommie C.    schedule 28.12.2018


Ответы (1)


Сначала вы можете создать расширение в качестве помощника:

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

Теперь вам нужно извлечь словарь данных из ответа и применить новые методы инициализации:

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

Если данные внезапно превращаются в массив, вам придется обрабатывать его по-другому (как массив User в этом примере)

person Y.Bonafons    schedule 21.09.2017
comment
Пожалуйста, объясните в комментарии, почему мой ответ не подходит, чтобы я мог заменить или улучшить его. Спасибо - person Y.Bonafons; 21.09.2017
comment
Понятия не имею, кто проголосовал против вашего ответа, но это был не я. Я прочитаю завтра утром и попробую. Сообщу, как все прошло :) - person Erez Hod; 22.09.2017