> Как справиться с JSON, если поля не совпадают со структурой в Swift (iOS, Swift)
Уровень: senior · Роль: mobile · Категория: Технические вопросы
Компании: Лингуа Лео
Стек: iOS, Swift
> Пример ответа
Короткий ответ
Основная стратегия - не привязывать модель данных напрямую к JSON. Используем промежуточные DTO (Data Transfer Object) с Codable, затем маппим в доменные модели. Для несовпадающих полей применяем CodingKeys для переименования, кастомный init(from:) для трансформаций, а для опциональных или отсутствующих ключей - decodeIfPresent и значения по умолчанию. Если структура JSON сильно отличается от модели, добавляем слой маппинга с явной логикой преобразования.
Подробное объяснение
Проблема возникает, когда сервер отдаёт поля с другими именами, типами, вложенностью или когда часть данных отсутствует. Прямое использование Codable с struct не всегда работает из-за строгой типизации Swift.
Основные инструменты:
CodingKeys- переименование ключей, если имена в JSON отличаются от свойств модели.init(from decoder:)- кастомная логика декодирования: преобразование типов, объединение полей, обработка отсутствующих значений.decodeIfPresent- для опциональных полей, чтобы не падать при отсутствии ключа.- Промежуточные DTO - когда JSON сложный или нестабильный, создаём структуры, повторяющие JSON, затем маппим в доменные модели.
JSONDecoderнастройки:keyDecodingStrategy = .convertFromSnakeCaseдля snake_case,dateDecodingStrategyдля дат.
Для полей, которые не совпадают по типу (например, строка вместо числа), используем кастомный init(from:) с ручным приведением. Если поле может отсутствовать - decodeIfPresent или ?? со значением по умолчанию.
На практике
На уровне senior важно не просто заставить код работать, а выбрать правильный уровень абстракции. Если JSON меняется часто - DTO обязателен. Если изменения редки и локальны - достаточно CodingKeys и кастомного инициализатора.
Также стоит учитывать: не нужно хранить сырые JSON-поля в доменной модели. Доменная модель должна быть чистой, без зависимости от формата передачи. Это упрощает тестирование и замену API.
Для больших проектов полезно выносить маппинг в отдельные extension или фабрики, чтобы не раздувать модели.
Пример кода
SWIFT// JSON: {"user_name": "John", "age_str": "30", "address": {"city": "Moscow"}}struct UserDTO: Decodable {let userName: Stringlet ageStr: Stringlet address: AddressDTOenum CodingKeys: String, CodingKey {case userName = "user_name"case ageStr = "age_str"case address}}struct AddressDTO: Decodable {let city: String}// Доменная модельstruct User {let name: Stringlet age: Intlet city: String}extension User {init(from dto: UserDTO) {self.name = dto.userNameself.age = Int(dto.ageStr) ?? 0self.city = dto.address.city}}// Использованиеlet decoder = JSONDecoder()let dto = try decoder.decode(UserDTO.self, from: data)let user = User(from: dto)
Пример с кастомным декодированием для нестандартного типа:
SWIFTstruct Product: Decodable {let id: Intlet price: Decimalenum CodingKeys: String, CodingKey {case id, price}init(from decoder: Decoder) throws {let container = try decoder.container(keyedBy: CodingKeys.self)id = try container.decode(Int.self, forKey: .id)// JSON отдаёт цену строкойlet priceString = try container.decode(String.self, forKey: .price)guard let value = Decimal(string: priceString) else {throw DecodingError.dataCorruptedError(forKey: .price,in: container,debugDescription: "Invalid price format")}price = value}}
Как отвечать на собеседовании
Начни с краткого ответа, затем переходи к деталям. Покажи, что понимаешь разницу между DTO и доменной моделью. Упомяни CodingKeys, decodeIfPresent, кастомный init(from:). Приведи пример из практики, где поля не совпадали, и объясни, почему выбрал тот или иной подход. Если спросят про производительность - отметь, что декодирование происходит один раз, а маппинг в доменные модели обычно дёшев.
Что проверяет интервьюер
- Понимание
Codableи его ограничений. - Умение проектировать слои данных.
- Навыки работы с опциональными и нестандартными полями.
- Способность объяснить trade-off между простотой и гибкостью.
- Внимание к деталям: обработка ошибок, значения по умолчанию.
Типичные ошибки
- Использование
try?без обработки ошибок - теряется диагностика. - Привязка доменной модели к JSON-структуре напрямую.
- Игнорирование
decodeIfPresent- падение при отсутствии ключа. - Хранение сырых строк вместо преобразования типов в
init(from:). - Слишком сложный маппинг там, где достаточно
CodingKeys. - Забывают про
keyDecodingStrategyдля snake_case.
> Похожие задачи по mobile
Приходилось ли определять органических и неорганических пользователей в приложении
Работали ли с подписками и покупками через StoreKit или другие сервисы
Что такое Set, его особенности, зачем нужен и какие есть реализации
Почему предпочтительнее использовать Dependency Injection вместо создания объектов вручную
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью