Serialización de objetos en Swift (iOS/macOS): Codable, JSON y PropertyList
La serialización es el proceso de convertir objetos de datos en un formato adecuado para almacenamiento o transmisión (por ejemplo, JSON, XML, Property List). En el ecosistema de Apple (iOS, macOS, watchOS, tvOS), Swift proporciona un mecanismo potente y elegante para la serialización y deserialización: el protocolo Codable. En este artículo, analizaremos en detalle cómo usar Codable, JSONEncoder, PropertyListEncoder y sus análogos para trabajar con datos.
¿Qué es Codable?
Codable es un protocolo compuesto que combina Encodable (para codificar un objeto a un formato externo) y Decodable (para decodificar desde un formato externo a un objeto). Si su estructura o clase Swift cumple con Codable, automáticamente obtiene la capacidad de serializarla y deserializarla.
Declaración básica:
struct User: Codable { let id: Int let name: String let email: String}Todos los tipos estándar de Swift (String, Int, Double, Bool, Date, Data, URL e incluso tipos opcionales y arreglos/diccionarios) ya cumplen con Codable.
Codificación a JSON
Para codificar un objeto a JSON se utiliza JSONEncoder. Ejemplo:
let user = User(id: 1, name: "Iván Petrov", email: "ivan@example.com")let encoder = JSONEncoder()encoder.outputFormatting = .prettyPrinted // para una salida bonita
do { let jsonData = try encoder.encode(user) if let jsonString = String(data: jsonData, encoding: .utf8) { print(jsonString) }} catch { print("Error de codificación: \\(error)")}Resultado:
{ "id" : 1, "name" : "Iván Petrov", "email" : "ivan@example.com"}Decodificación desde JSON
Para la transformación inversa se utiliza JSONDecoder:
let jsonString = """{ "id": 2, "name": "María Sidorova", "email": "maria@example.com"}"""let jsonData = Data(jsonString.utf8)let decoder = JSONDecoder()
do { let decodedUser = try decoder.decode(User.self, from: jsonData) print("Usuario decodificado: \\(decodedUser.name)")} catch { print("Error de decodificación: \\(error)")}Trabajo con Property List (plist)
Para la serialización al formato XML Property List se utilizan PropertyListEncoder y PropertyListDecoder. Esto es útil para almacenar datos en archivos de configuración o UserDefaults.
// Codificación a plistlet encoder = PropertyListEncoder()encoder.outputFormat = .xml
do { let plistData = try encoder.encode(user) // plistData se puede guardar en un archivo o en UserDefaults} catch { print("Error de codificación plist: \\(error)")}
// Decodificación desde plistlet decoder = PropertyListDecoder()do { let decodedUser = try decoder.decode(User.self, from: plistData) print(decodedUser)} catch { print("Error de decodificación plist: \\(error)")}Claves personalizadas (CodingKeys)
A veces los nombres de las propiedades en Swift no coinciden con las claves en JSON. Para esto se utiliza la enumeración CodingKeys:
struct Product: Codable { let productId: Int let productName: String let price: Double
enum CodingKeys: String, CodingKey { case productId = "product_id" case productName = "product_name" case price }}Ahora el JSON con las claves product_id y product_name será procesado correctamente.
Manejo de fechas y otros tipos especiales
Por defecto, JSONEncoder y JSONDecoder codifican las fechas como TimeInterval