Добавить объявление

Декодирование JSON в Swift: работа с датой

Одна из самых частых проблем при работе с API - даты. Казалось бы, что может пойти не так? Но на практике бэкенд может прислать timestamp, ISO-строку, американский формат или вообще что-то свое. Swift без правильной настройки просто упадет с ошибкой. Разбираемся, какие стратегии декодирования есть и когда что применять.

Что Swift делает по умолчанию:


Стандартная стратегия .deferredToDate работает только в одном случае: если данные были закодированы самим JSONEncoder в нативном для Apple формате. В реальной жизни такое встречается редко. Чаще всего сервер присылает либо Unix timestamp, либо строку в ISO 8601, либо что-то кастомное. И здесь без явного указания стратегии не обойтись.

Timestamp - секунды или миллисекунды:


Если API отдает число - скорее всего, это timestamp. Для секунд с 1970 года подходит .secondsSince1970, для миллисекунд - .millisecondsSince1970. Главное, чтобы бэкенд был последователен: если в одном поле миллисекунды, а в другом секунды - придется писать кастомную логику.

ISO 8601 - встроенная поддержка:


Для дат вроде "2025-11-01T12:00:00Z" есть готовая стратегия .iso8601. Но у нее есть нюанс: она довольно строгая. Если сервер пришлет миллисекунды или немного другой формат часового пояса, декодирование упадет. В таких случаях помогает кастомный ISO8601DateFormatter с ослабленными правилами.

Кастомный DateFormatter:


Когда формат не вписывается ни в одну стандартную стратегию, можно задать свой форматер:

let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)

decoder.dateDecodingStrategy = .formatted(formatter)

Важно явно указывать локаль и часовой пояс. Иначе форматтер будет опираться на настройки устройства, и на телефоне с русской локалью дата может не распарситься.

Когда форматов несколько:


Бывает, что API в разных полях или даже в одном поле присылает даты в разных форматах. Например, новые данные в ISO, старые - в timestamp. Или просто разработчики бэкенда не смогли договориться.

В таких случаях выручает кастомная стратегия, где можно перебрать несколько форматов и вернуть первую успешно распарсенную дату:

decoder.dateDecodingStrategy = .custom { decoder in
let container = try decoder.singleValueContainer()
let string = try container.decode(String.self)

let formatters: [DateFormatter] = [isoFormatter, legacyFormatter]

for formatter in formatters {
if let date = formatter.date(from: string) {
return date
}
}

throw DecodingError.dataCorruptedError(
in: container,
debugDescription: "Неизвестный формат даты: \(string)"
)
}

Вся логика остается в одном месте, а не размазывается по моделям.

Вывод:


Даты - одно из тех мест, где декодинг может упасть в самый неожиданный момент. Хорошая новость: Swift дает гибкие инструменты, чтобы разрулить почти любую ситуацию. Плохая: о них нужно знать и применять осознанно. Универсальной стратегии нет, но правильно настроенный декодер спасет от кучи багов на проде.
12.03.2026 20 164