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

это должно заставить все это работать.