Premise
У меня есть struct
, который соответствует Decodable
, поэтому он может декодировать JSON из различных ответов через init(from:)
. Для каждого типа ответа JSON, который я ожидаю декодировать, у меня есть enum
, который соответствует CodingKey
.
Пример
Вот упрощенный пример, который можно опустить в Swift Playground:
import Foundation
// MARK: - Services -
struct Service1 {}
struct Service2 {}
// MARK: - Person Model -
struct Person {
let name: String
}
extension Person: Decodable {
enum CodingKeys: String, CodingKey {
case name = "name"
}
enum Service2CodingKeys: String, CodingKey {
case name = "person_name"
}
// And so on through service n...
init(from decoder: Decoder) throws {
switch decoder.userInfo[.service] {
case is Service1.Type:
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
case is Service2.Type:
let container = try decoder.container(keyedBy: Service2CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
// And so on through service n...
default:
fatalError("Missing implementation for service.")
}
}
}
// MARK: - CodingUserInfoKey -
extension CodingUserInfoKey {
static let service = CodingUserInfoKey(rawValue: "service")!
}
// MARK: - Responses -
// The JSON response from service 1.
let service1JSONResponse = """
[
{
"name": "Peter",
}
]
""".data(using: .utf8)!
// The JSON response from service 2.
let service2JSONResponse = """
[
{
"person_name": "Paul",
}
]
""".data(using: .utf8)!
// And so on through service n... where other services have JSON responses with keys of varied names ("full_name", "personName").
// MARK: - Decoding -
let decoder = JSONDecoder()
decoder.userInfo[.service] = Service1.self
let service1Persons = try decoder.decode([Person].self, from: service1JSONResponse)
decoder.userInfo[.service] = Service2.self
let service2Persons = try decoder.decode([Person].self, from: service2JSONResponse)
Проблема
Проблема, с которой я сталкиваюсь, заключается в том, что у меня есть множество различных сервисов, из которых мне нужно было декодировать ответы, и модель со многими другими свойствами. чем этот упрощенный пример. По мере увеличения количества служб увеличивается и количество дел, необходимых для декодирования этих ответов.
Вопрос
Как можно упростить мою реализацию init(from:)
, чтобы уменьшить все это дублирование кода?
Попытки
Я пытался сохранить правильный CodingKey.Type
для каждой службы и передать его в container(keyedBy:)
, но я получаю эту ошибку:
Невозможно вызвать ' контейнер "со списком аргументов типа" (keyedBy: CodingKey.Type) ".
init(from decoder: Decoder) throws {
let codingKeyType: CodingKey.Type
switch decoder.userInfo[.service] {
case is Service1.Type: codingKeyType = CodingKeys.self
case is Service2.Type: codingKeyType = Service2CodingKeys.self
default: fatalError("Missing implementation for service.")
}
let container = try decoder.container(keyedBy: codingKeyType) // ← Error
name = try container.decode(String.self, forKey: .name)
}