Как следует из других ответов, вы можете декодировать Data
или Base-64 String
после того, как JSONSerialization
или JSONDecoder
декодировали результаты API.
Но если вы предпочитаете писать инициализатор декодирования, вы можете сделать это следующим образом:
Возможно, это не сильно отличается от вашего Response
.
struct Response: Codable {
var responseCode: Int
var results: [Result]
enum CodingKeys: String, CodingKey {
case responseCode = "response_code"
case results
}
}
Чтобы подготовиться к написанию инициализатора декодирования для Response
, я хотел бы использовать некоторые расширения:
extension KeyedDecodingContainer {
func decodeBase64(forKey key: Key, encoding: String.Encoding) throws -> String {
guard let string = try self.decode(String.self, forKey: key).decodeBase64(encoding: encoding) else {
throw DecodingError.dataCorruptedError(forKey: key, in: self,
debugDescription: "Not a valid Base-64 representing UTF-8")
}
return string
}
func decodeBase64(forKey key: Key, encoding: String.Encoding) throws -> [String] {
var arrContainer = try self.nestedUnkeyedContainer(forKey: key)
var strings: [String] = []
while !arrContainer.isAtEnd {
guard let string = try arrContainer.decode(String.self).decodeBase64(encoding: encoding) else {
throw DecodingError.dataCorruptedError(forKey: key, in: self,
debugDescription: "Not a valid Base-64 representing UTF-8")
}
strings.append(string)
}
return strings
}
}
Используя эти расширения выше, вы можете определить тип Result
следующим образом:
extension Response {
struct Result: Codable {
var category: String
var type: String
var difficulty: String
var question: String
var correctAnswer: String
var incorrectAnswers: [String]
enum CodingKeys: String, CodingKey {
case category
case type
case difficulty
case question
case correctAnswer = "correct_answer"
case incorrectAnswers = "incorrect_answers"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.category = try container.decodeBase64(forKey: .category, encoding: .utf8)
self.type = try container.decodeBase64(forKey: .type, encoding: .utf8)
self.difficulty = try container.decodeBase64(forKey: .difficulty, encoding: .utf8)
self.question = try container.decodeBase64(forKey: .question, encoding: .utf8)
self.correctAnswer = try container.decodeBase64(forKey: .correctAnswer, encoding: .utf8)
self.incorrectAnswers = try container.decodeBase64(forKey: .incorrectAnswers, encoding: .utf8)
}
}
}
(Вы не упомянули, определен ли ваш Response
(или другое имя?) Как вложенный тип или нет, но я думаю, что вы можете переименовать или изменить его самостоятельно.)
Учитывая все вышесказанное, вы можете просто декодировать ответ API как:
do {
let decoder = JSONDecoder()
let questionData = try decoder.decode(Response.self, from: data)
print(questionData)
} catch {
print("Error", error)
}
Кстати, вы говорите Я думаю, что лучшее решение - это использовать кодирование base64 (так как оно поддерживается в Swift) , но так ли это на самом деле?
Base-64 до Data
поддерживается в JSONDecoder
, но это не то, что вы ожидаете. Таким образом, использование другой кодировки может быть лучшим выбором.
Но, в любом случае, строка JSON может представлять все символы Юникода, используя только ASCII с \uXXXX
или \uHHHH\uLLLL
. Поэтому я не понимаю, почему разработчики API не предоставляют опцию Стандартная кодировка JSON . Если вы можете связаться с ними, скажите им, чтобы они предоставили опцию, которая может упростить многие клиентские коды.