Swift 4 Codable Array's
Итак, у меня есть маршрут API, который возвращает массив объектов JSON. Например:
[
{"firstname": "Tom", "lastname": "Smith", "age": 31},
{"firstname": "Bob", "lastname": "Smith", "age": 28}
]
Я пытаюсь представить, как использовать новую настраиваемую функцию в Swift для преобразования этих двух объектов в класс. Поэтому, если у меня есть класс человека, который можно кодировать, я бы хотел принять этот ответ и дать ему два объекта.
Я также использую Alamofire для обработки запросов.
Как я могу это сделать? До сих пор все, что я видел, связанное с кодируемым материалом, допускает только один объект. И я не видел интеграции с Alamofire или веб-каркасом.
Ответы
Ответ 1
Обновленная информация о Alamofire 5: responseJSONDecodable
.
struct Person: Codable {
let firstName, lastName: String
let age: Int
enum CodingKeys : String, CodingKey {
case firstName = "firstname"
case lastName = "lastname"
case age
}
}
Alamofire.request(request).responseJSONDecodable { (response: DataResponse<Person>) in
print(response)
}
Alamofire 4 пока не добавляет поддержку Codable (см. # 2177), вместо этого вы можете использовать это расширение: https://github.com/Otbivnoe/CodableAlamofire.
let jsonData = """
[
{"firstname": "Tom", "lastname": "Smith", "age": 31},
{"firstname": "Bob", "lastname": "Smith", "age": 28}
]
""".data(using: .utf8)!
struct Person: Codable {
let firstName, lastName: String
let age: Int
enum CodingKeys : String, CodingKey {
case firstName = "firstname"
case lastName = "lastname"
case age
}
}
let decoded = try! JSONDecoder().decode([Person].self, from: jsonData)
Образец: http://swift.sandbox.bluemix.net/#/repl/59a4b4fad129044611590820
Использование CodableAlamofire:
let decoder = JSONDecoder()
Alamofire.request(url).responseDecodableObject(keyPath: nil, decoder: decoder) { (response: DataResponse<[Person]>) in
let persons = response.result.value
print(persons)
}
keypath
соответствует пути, в котором результаты содержатся в структуре JSON. Например:
{
"result": {
"persons": [
{"firstname": "Tom", "lastname": "Smith", "age": 31},
{"firstname": "Bob", "lastname": "Smith", "age": 28}
]
}
}
keypath
=> results.persons
[
{"firstname": "Tom", "lastname": "Smith", "age": 31},
{"firstname": "Bob", "lastname": "Smith", "age": 28}
]
keypath
=> nil
(пустой keypath
выдает исключение)
Ответ 2
Мне удалось сериализовать ответ данных на кодируемые объекты.
Как вы могли быть знакомы с преобразованием json-объекта [String: String]
, например. Этот объект json нужно преобразовать в Data
с помощью json.data(using: .utf8)!
.
В Alamofire легко получить данные (или, по крайней мере, такие данные для меня, уже совместимые с темой .utf8
), я могу просто использовать эту уже доступную функцию
func responseData(queue: DispatchQueue?, completionHandler: @escaping (DataResponse<Data>) -> Void) -> Self
Затем просто используйте эти данные в качестве входных данных для Decoder
в completionHandler
let objek = try JSONDecoder().decode(T.self, from: data)
Вы также можете сделать это с помощью некоторой общей функции сериализации с небольшой настройкой из документации
Сериализация объектов типового ответа
к этой модификации
func responseCodable<T: Codable>(
queue: DispatchQueue? = nil,
completionHandler: @escaping (DataResponse<T>) -> Void)
-> Self
{
let responseSerializer = DataResponseSerializer<T> { request, response, data, error in
guard error == nil else { return .failure(BackendError.network(error: error!)) }
guard let data = data else {
return .failure(BackendError.objectSerialization(reason: "data is not valid"))
}
do{
let objek = try JSONDecoder().decode(T.self, from: data!)
return .success(objek)
} catch let e {
return .failure(BackendError.codableSerialization(error: e))
}
}
return response(queue: queue, responseSerializer: responseSerializer, completionHandler: completionHandler)
}
Пример структуры
struct Fids: Codable {
var Status: Status?
var Airport: Airport?
var Record: [FidsRecord]
}
Используйте функцию таким образом
Alamofire.request("http://whatever.com/zzz").responseCodable { (response: DataResponse<Fids>) in
switch response.result{
case .success(let value):
print(value.Airport)
// MARK: do whatever you want
case .failure(let error):
print(error)
self.showToast(message: error.localizedDescription)
}
}
Ответ 3
для декодирования в массив, ваш ответ в псевдониме типа для ясности:
typealias ServiceResponseObject = [ResponseObject]
но тогда вам придется подтвердить Array для кодируемого:
extension Array: Decodable where Element: Decodable {}
это должно заставить все это работать.