Сериализация объектов в Swift (iOS/macOS)
Сериализация — это процесс преобразования сложных объектов (структур, классов, экземпляров) в формат, удобный для хранения или передачи по сети (JSON, Property List, бинарные данные). Десериализация — обратный процесс восстановления объекта из этих данных. В Swift для iOS и macOS существуют два основных подхода: современный протокол Codable (и его составляющие Encodable и Decodable) и более старый NSCoding из фреймворка Foundation. В этой статье мы подробно разберем оба подхода с примерами кода.
1. Протокол Codable (рекомендованный способ)
Codable — это протокол, объединяющий Encodable и Decodable. Он позволяет автоматически генерировать код для сериализации и десериализации, если все свойства вашей структуры или класса также соответствуют Codable (строки, числа, даты, массивы, словари и т.д.).
1.1. Базовая сериализация в JSON
Рассмотрим простую структуру пользователя:
import Foundation
struct User: Codable {
let id: Int
var name: String
var email: String
}
// Создаем экземпляр
let user = User(id: 1, name: "Иван Петров", email: "ivan@example.com")
// Сериализация в Data (JSON)
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted // для читаемого вывода
do {
let jsonData = try encoder.encode(user)
if let jsonString = String(data: jsonData, encoding: .utf8) {
print(jsonString)
}
} catch {
print("Ошибка сериализации: \(error.localizedDescription)")
}
1.2. Десериализация из JSON
// Предположим, у нас есть JSON-строка
let jsonString = """
{
"id": 2,
"name": "Мария Смирнова",
"email": "maria@test.com"
}
"""
let jsonData = Data(jsonString.utf8)
let decoder = JSONDecoder()
do {
let decodedUser = try decoder.decode(User.self, from: jsonData)
print("Имя: \(decodedUser.name), Email: \(decodedUser.email)")
} catch {
print("Ошибка десериализации: \(error.localizedDescription)")
}
1.3. Настройка ключей (CodingKeys)
Если имена свойств в Swift отличаются от ключей в JSON, используйте перечисление CodingKeys:
struct Article: Codable {
var title: String
var body: String
var publicationDate: Date
enum CodingKeys: String, CodingKey {
case title
case body
case publicationDate = "published_at" // ключ в JSON
}
}
let articleJSON = """
{"title":"Swift Codable","body":"Текст статьи","published_at":"2024-01-15T10:00:00Z"}
"""
let articleData = Data(articleJSON.utf8)
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601 // для корректного парсинга даты
if let article = try? decoder.decode(Article.self, from: articleData) {
print("Заголовок: \(article.title), Дата: \(article.publicationDate)")
}
1.4. PropertyListEncoder / PropertyListDecoder
Для сериализации в формат Property List (XML или бинарный) используйте PropertyListEncoder:
let plistEncoder = PropertyListEncoder()
plistEncoder.outputFormat = .xml
if let plistData = try? plistEncoder.encode(user) {
// plistData можно сохранить в файл
let plistString = String(data: plistData, encoding: .utf8)
print(plistString ?? "Некорректные данные")
}
2. Протокол NSCoding (для классов, наследуемых от NSObject)
NSCoding — это протокол из Objective-C, который до сих пор используется для сериализации классов, особенно тех, что работают с UIKit/AppKit (например, UIView, UIViewController). Он требует, чтобы класс наследовал от NSObject и реализовывал методы encode(with:) и init?(coder:).
2.1. Пример класса с NSCoding
import Foundation
class Person: NSObject, NSCoding {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
// MARK: - NSCoding
func encode(with coder: NSCoder) {
coder.encode(name, forKey: "name")
coder.encode(age, forKey: "age")
}
required init?(coder: NSCoder) {
guard let name = coder.decodeObject(forKey: "name") as? String else {
return nil
}
self.name = name
self.age = coder.decodeInteger(forKey: "age")
}
}
// Сериализация
let person = Person(name: "Алексей", age: 30)
let data = NSKeyedArchiver.archived