Работа с JSON в Swift: парсинг и сериализация для iOS и macOS

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Работа с JSON в Swift: парсинг и сериализация

JSON (JavaScript Object Notation) — это легковесный формат обмена данными, широко используемый в веб-сервисах и мобильных приложениях. В экосистеме Apple (iOS, macOS, watchOS, tvOS) работа с JSON является неотъемлемой частью разработки: от загрузки данных с сервера до сохранения настроек приложения. В этой статье мы подробно рассмотрим, как выполнять парсинг (декодирование) и сериализацию (кодирование) JSON в Swift, используя встроенные протоколы Codable, JSONDecoder и JSONEncoder.

1. Основы Codable: модель данных

Современный подход к работе с JSON в Swift базируется на протоколе Codable, который объединяет два протокола: Encodable (для сериализации) и Decodable (для парсинга). Чтобы ваша структура или класс могли быть преобразованы в JSON и обратно, достаточно объявить их как Codable.

Рассмотрим пример модели пользователя:

struct User: Codable {
    let id: Int
    let name: String
    let email: String
    let isActive: Bool
}

Если ключи в JSON отличаются от названий свойств в Swift (например, используется snake_case вместо camelCase), вы можете использовать перечисление CodingKeys:

struct User: Codable {
    let id: Int
    let fullName: String
    let email: String
    let isActive: Bool

    enum CodingKeys: String, CodingKey {
        case id
        case fullName = "full_name"
        case email
        case isActive = "is_active"
    }
}

2. Парсинг JSON с помощью JSONDecoder

Для преобразования JSON-строки или данных в объекты Swift используется JSONDecoder. Основной метод — decode(_:from:), который принимает тип данных (соответствующий Decodable) и объект Data.

Пример парсинга JSON-строки, полученной от API:

let jsonString = """
{
    "id": 1,
    "name": "Иван Петров",
    "email": "ivan@example.com",
    "is_active": true
}
"""

let jsonData = Data(jsonString.utf8)

do {
    let decoder = JSONDecoder()
    let user = try decoder.decode(User.self, from: jsonData)
    print("Пользователь: \(user.name), email: \(user.email)")
} catch {
    print("Ошибка декодирования: \(error.localizedDescription)")
}

Часто JSON приходит в виде массива объектов. В этом случае просто укажите тип [User].self:

let jsonArrayString = """
[
    {"id": 1, "name": "Иван", "email": "ivan@test.com", "is_active": true},
    {"id": 2, "name": "Мария", "email": "maria@test.com", "is_active": false}
]
"""

let jsonArrayData = Data(jsonArrayString.utf8)

do {
    let users = try JSONDecoder().decode([User].self, from: jsonArrayData)
    users.forEach { print($0.name) }
} catch {
    print("Ошибка: \(error)")
}

3. Сериализация объектов в JSON с JSONEncoder

Обратный процесс — преобразование объекта Swift в JSON — выполняется с помощью JSONEncoder. Метод encode(_:) возвращает объект Data, который затем можно преобразовать в строку или отправить на сервер.

let user = User(id: 3, fullName: "Алексей Смирнов", email: "alex@example.com", isActive: true)

do {
    let encoder = JSONEncoder()
    encoder.outputFormatting = .prettyPrinted // для читаемого форматирования
    let jsonData = try encoder.encode(user)
    if let jsonString = String(data: jsonData, encoding: .utf8) {
        print(jsonString)
    }
} catch {
    print("Ошибка кодирования: \(error)")
}

Вывод будет выглядеть так:

{
  "id" : 3,
  "full_name" : "Алексей Смирнов",
  "email" : "alex@example.com",
  "is_active" : true
}

Обратите внимание, что JSONEncoder автоматически использует ключи из CodingKeys (если они заданы).

4. Работа с вложенными структурами и опциональными полями

Реальные JSON-данные часто содержат вложенные объекты, массивы и опциональные поля. Swift Codable легко справляется с этим.

Пример модели с вложенными данными:

struct Company: Codable {
    let name: String
    let employees: [Employee]
}

struct Employee: Codable {
    let id: Int
    let name: String
    let position: String?
    let salary: Double?
}

let jsonCompany = """
{
    "name": "TechCorp",
    "employees": [
        {"id": 1, "name": "Ольга", "position": "iOS Developer", "salary": 120000},
        {"id": 2, "name": "Дмитрий", "position": nil, "salary": null}
    ]
}
"""

let companyData = Data(jsonCompany.utf8)

do {
    let company = try JSONDecoder().decode(Company.self, from: companyData)
    for employee in company.employees {
        let pos = employee.position ?? "Не указана"
        let sal = employee.salary != nil ? "\(employee.salary!) руб." : "Не указана"
        print("\(employee.name): \(pos), зарплата: \(sal)")
    }
} catch {
    print("Ошибка: \(error)")
}

В этом примере поля position и salary объявлены как опциональные (?), поэтому Codable корректно обработает null в JSON.

5. Обработка ошибок и кастомные стратегии

При работе с JSON важно корректно обрабатывать ошибки. JSONDecoder предоставляет несколько полезных свойств для настройки парсинга:

  • keyDecodingStrategy — позволяет автоматически конвертировать ключи (например, из snake_case в camelCase).
  • dateDecodingStrategy — задает формат для парсинга дат.
  • dataDecodingStrategy — определяет, как декодировать бинарные данные.
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
decoder.dateDecodingStrategy = .iso8601

struct Event: Codable {
    let eventName: String
    let eventDate: Date
}

let jsonEvent = """
{"event_name": "WWDC", "                

Блоги

Рекомендаций по книгам