Работа с JSON в Swift: парсинг и сериализация на практике
JSON (JavaScript Object Notation) — это легковесный формат обмена данными, который стал стандартом де-факто для взаимодействия между клиентом и сервером в мобильной и веб-разработке. В экосистеме Apple (iOS, macOS, watchOS, tvOS) работа с JSON осуществляется через мощные встроенные механизмы. В этой статье мы подробно разберём, как парсить и сериализовать JSON в Swift на реальных примерах, используя как современный протокол Codable, так и классический JSONSerialization.
Статья предназначена для разработчиков, которые уже знакомы с основами Swift и хотят уверенно работать с сетевыми запросами и локальным хранением данных в формате JSON.
1. Основы JSON и его место в Swift-приложениях
JSON представляет собой текстовый формат, основанный на двух структурах: объекты (набор пар ключ-значение) и массивы (упорядоченные списки). В Swift этим структурам соответствуют Dictionary и Array. Типы данных в JSON ограничены: строки, числа, булевы значения, null, объекты и массивы.
Типичный сценарий использования JSON в iOS/macOS приложении выглядит так:
- Получение JSON-строки от сервера (через URLSession).
- Парсинг JSON в модели Swift (например, структуры или классы).
- Обработка данных и отображение их в интерфейсе.
- Сериализация (обратное преобразование) моделей в JSON для отправки на сервер.
Apple предоставляет два основных подхода для работы с JSON: JSONSerialization (низкоуровневый, но гибкий) и Codable (высокоуровневый, типобезопасный, рекомендованный с Swift 4+).
2. Парсинг JSON с помощью Codable (рекомендуемый способ)
Протокол Codable объединяет Encodable (для сериализации) и Decodable (для парсинга). Это самый простой и безопасный способ работы с JSON в Swift.
Пример 1: Парсинг простого JSON-объекта
Допустим, сервер возвращает следующий JSON:
{
"userId": 1,
"id": 1,
"title": "Пример задачи",
"completed": false
}
Создадим структуру модели, соответствующую Codable:
struct TodoItem: Codable {
let userId: Int
let id: Int
let title: String
let completed: Bool
}
Теперь выполним парсинг JSON-строки:
let jsonString = """
{
"userId": 1,
"id": 1,
"title": "Пример задачи",
"completed": false
}
"""
if let jsonData = jsonString.data(using: .utf8) {
let decoder = JSONDecoder()
do {
let todo = try decoder.decode(TodoItem.self, from: jsonData)
print("Задача: \(todo.title), выполнена: \(todo.completed)")
} catch {
print("Ошибка парсинга: \(error.localizedDescription)")
}
}
Пример 2: Парсинг массива объектов
Если сервер возвращает массив задач:
[
{"userId": 1, "id": 1, "title": "Задача 1", "completed": false},
{"userId": 1, "id": 2, "title": "Задача 2", "completed": true}
]
Парсинг выполняется так же просто — нужно указать тип массива:
let todoArray = try decoder.decode([TodoItem].self, from: jsonData)
2.1. Кастомные ключи и snake_case
Часто JSON использует формат snake_case (например, user_id), а в Swift принят camelCase (userId). JSONDecoder умеет преобразовывать ключи автоматически:
decoder.keyDecodingStrategy = .convertFromSnakeCase
Если нужно задать нестандартное соответствие, используйте перечисление CodingKeys:
struct User: Codable {
let fullName: String
let emailAddress: String
enum CodingKeys: String, CodingKey {
case fullName = "full_name"
case emailAddress = "email_address"
}
}
3. Сериализация объектов в JSON с помощью Codable
Обратный процесс — преобразование Swift-объекта в JSON — выполняется с помощью JSONEncoder.
Пример: Сериализация структуры в JSON
let newTodo = TodoItem(userId: 1, id: 3, title: "Новая задача", completed: false)
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted // для читаемого вывода
do {
let jsonData = try encoder.encode(newTodo)
if let jsonString = String(data: jsonData, encoding: .utf8) {
print(jsonString)
}
} catch {
print("Ошибка сериализации: \(error)")
}
Результат выполнения:
{
"userId" : 1,
"id" : 3,
"title" : "Новая задача",
"completed" : false
}
Для отправки на сервер JSON обычно передаётся как тело HTTP-запроса. Пример с URLSession:
var request = URLRequest(url: URL(string: "https://api.example.com/todos")!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try encoder.encode(newTodo)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
// обработка ответа
}
task.resume()
4. Работа с JSONSerialization (для сложных или динамических данных)
Несмотря на всю мощь Codable, иногда требуется обработать JSON с неизвестной структурой или смешанными типами. В таких случаях на помощь приходит JSONSerialization.
Пример: Парсинг с JSONSerialization
if let jsonData = jsonString.data(using: .utf8) {
do {
if let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] {
// Работаем с Dictionary
if let title = jsonObject["title"] as? String {
print("Title: \(title)")
}
} else if let jsonArray = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [[String: Any]] {
// Работаем с ма