Source
Затишна Галера | iOSКомпас 🧭1️⃣7️⃣3️⃣ Завдання 173Як правильно декодувати JSON у Swift ...
407 Views/Reach
2026-09-23 08:26
Message №2977
#iOSКомпас 🧭
1️⃣7️⃣3️⃣ Завдання 173
Як правильно декодувати JSON у Swift для роботи з датою?
Доброго здровля мої любі друзі. З вами ваш незмінний ios розробник Сергій з @badlinkschannel.
Одна з найчастіших проблем під час роботи з API - дати. Здавалося б, що може піти не так? Але на практиці бекенд може надіслати timestamp, ISO-рядок, американський формат або взагалі щось своє. Swift без правильної настройки декодера просто поверне помилку. Розбираємося, які стратегії декодування є і коли яку застосовувати.
🔣 Що Swift робить за замовчуванням:
Стандартна стратегія .deferredToDate використовує нативне `Codable`-представлення Date. Саме таку пару форматів за замовчуванням використовують JSONEncoder і JSONDecoder, але в реальних API це трапляється рідко. Найчастіше сервер надсилає або Unix timestamp, або рядок в ISO 8601, або щось кастомне. І тут без явного вибору стратегії не обійтися.
🔣 Timestamp - секунди чи мілісекунди:
Якщо API віддає дату числом - це часто timestamp. Для секунд від 1970 року підходить .secondsSince1970, для мілісекунд - .millisecondsSince1970. Головне, щоб бекенд був послідовним: якщо в одному полі мілісекунди, а в іншому секунди - доведеться писати кастомну логіку.
🔣 ISO 8601 - вбудована підтримка:
Для дат на кшталт "2025-11-01T12:00:00Z" є готова стратегія .iso8601. У сучасному Foundation вона вже нормально працює і з fractional seconds. Але якщо сервер надсилає нестандартний ISO-формат або взагалі відхиляється від RFC 3339, декодування все одно може впасти. У таких випадках допомагає кастомний парсер або 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()
if let timestamp = try? container.decode(Double.self) {
return Date(timeIntervalSince1970: timestamp)
}
let string = try container.decode(String.self)
for formatter in formatters {
if let date = formatter.date(from: string) {
return date
}
}
throw DecodingError.dataCorruptedError(
in: container,
debugDescription: "Невідомий формат дати: \(string)"
)
}
Уся логіка залишається в одному місці, а не розмазується по моделях.
#️⃣ Дати - одне з тих місць, де декодинг може впасти в найнесподіваніший момент. Хороша новина: Swift дає гнучкі інструменти, щоб розрулити майже будь-яку ситуацію. Погана: про них потрібно знати й застосовувати усвідомлено. Універсальної стратегії немає, але правильно налаштований декодер врятує від купи багів на проді.
@Zatishna_Galera
1️⃣7️⃣3️⃣ Завдання 173
Як правильно декодувати JSON у Swift для роботи з датою?
Доброго здровля мої любі друзі. З вами ваш незмінний ios розробник Сергій з @badlinkschannel.
Одна з найчастіших проблем під час роботи з API - дати. Здавалося б, що може піти не так? Але на практиці бекенд може надіслати timestamp, ISO-рядок, американський формат або взагалі щось своє. Swift без правильної настройки декодера просто поверне помилку. Розбираємося, які стратегії декодування є і коли яку застосовувати.
🔣 Що Swift робить за замовчуванням:
Стандартна стратегія .deferredToDate використовує нативне `Codable`-представлення Date. Саме таку пару форматів за замовчуванням використовують JSONEncoder і JSONDecoder, але в реальних API це трапляється рідко. Найчастіше сервер надсилає або Unix timestamp, або рядок в ISO 8601, або щось кастомне. І тут без явного вибору стратегії не обійтися.
🔣 Timestamp - секунди чи мілісекунди:
Якщо API віддає дату числом - це часто timestamp. Для секунд від 1970 року підходить .secondsSince1970, для мілісекунд - .millisecondsSince1970. Головне, щоб бекенд був послідовним: якщо в одному полі мілісекунди, а в іншому секунди - доведеться писати кастомну логіку.
🔣 ISO 8601 - вбудована підтримка:
Для дат на кшталт "2025-11-01T12:00:00Z" є готова стратегія .iso8601. У сучасному Foundation вона вже нормально працює і з fractional seconds. Але якщо сервер надсилає нестандартний ISO-формат або взагалі відхиляється від RFC 3339, декодування все одно може впасти. У таких випадках допомагає кастомний парсер або 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()
if let timestamp = try? container.decode(Double.self) {
return Date(timeIntervalSince1970: timestamp)
}
let string = try container.decode(String.self)
for formatter in formatters {
if let date = formatter.date(from: string) {
return date
}
}
throw DecodingError.dataCorruptedError(
in: container,
debugDescription: "Невідомий формат дати: \(string)"
)
}
Уся логіка залишається в одному місці, а не розмазується по моделях.
#️⃣ Дати - одне з тих місць, де декодинг може впасти в найнесподіваніший момент. Хороша новина: Swift дає гнучкі інструменти, щоб розрулити майже будь-яку ситуацію. Погана: про них потрібно знати й застосовувати усвідомлено. Універсальної стратегії немає, але правильно налаштований декодер врятує від купи багів на проді.
@Zatishna_Galera