Как реализовать поддержку кодирования и декодирования JSON без классов в Swift
Что проверяют
Что вы понимаете под «без классов» и знаете ли, что Codable прекрасно работает со структурами и перечислениями, а наследование от NSObject для этого не нужно. Второй смысл вопроса: как разобрать JSON, для которого вообще нет модели. Следом спрашивают про нестандартные ключи, даты и обработку ошибок.
Как отвечать
- структуры с
Codable:struct User: Codableс полями, соответствующими JSON. Компилятор сам синтезируетinit(from:)иencode(to:), если все поля тожеCodable. Кодирование черезJSONEncoder().encode(user), разбор черезJSONDecoder().decode(User.self, from: data); - ключи и форматы:
keyDecodingStrategy = .convertFromSnakeCaseпревращаетuser_nameвuserName, для отдельных полей пишетсяenum CodingKeys: String, CodingKey. Даты настраиваются черезdateDecodingStrategy, например.iso8601; - перечисления: enum с raw value декодируется из строки или числа, а начиная со Swift 5.5 синтез работает и для enum со связанными значениями;
- своя логика: ручной
init(from:), когда поле приходит то строкой, то числом, или нужен дефолт при отсутствии ключа; - JSON без модели: рекурсивный
enum JSONValue: Codableс кейсамиstring,number,bool,null,array([JSONValue]),object([String: JSONValue]). Он типобезопасен, в отличие отJSONSerialization, который возвращаетAnyи заставляет кастить вручную.
Структуры здесь выигрывают не только стилем: это типы-значения, их безопасно передавать между потоками, и в Swift 6 они проще проходят проверку Sendable.
Где спотыкаются
Пишут try? и теряют причину. DecodingError подробно сообщает, что не так: keyNotFound, typeMismatch, valueNotFound с путём до поля. Это экономит часы.
Ожидают, что значение по умолчанию в свойстве сработает при отсутствии ключа. Синтезированный декодер его не использует: var limit: Int = 10 без ключа в JSON даст keyNotFound. Нужно сделать поле опциональным или написать свой init(from:).
Используют JSONSerialization и кастят через as! [String: Any]. Любое изменение ответа сервера роняет приложение, а булевы значения и числа приходят как NSNumber и путаются между собой.