Con el nuevo XCode 9 y Swift 4 hemos visto algunos cambios en la forma de desarrollar en Swift. Uno de los cambios que más están sorprendiendo actualmente por su eficacia es el uso de los tipos Encodable y Decodable que básicamente hace el trabajo sucio de parsear un fichero JSON. Es bastante fácil de usar y aplicable a cualquier arquitectura.
¿Qué encontrarás en este post?
Toggle1. ¿Que es Encodable y Decodable?
Encodable es un tipo que se puede transformar a si mismo para representarse externamente. Básicamente es un objeto que es capaz de transformarse en un fichero JSON. Decodable es un tipo que puede transformar una representación externa en un objeto. Eso significa que puede interpretar un JSON y transformarlo en objetos de tipo decodable.2. ¿Entonces por que Codable?
Codable es simplemente un conjunto de los dos anteriores. Cuando implementamos de Codable en realidad estamos haciendo lo mismo que si implementamos de Encodable y Decodabletypealias codable = Decodable & Encodable
3. Objeto Codable
Codable se puede implementar desde las estructuras class, struct y enum. Para formar un objeto codable partiremos del siguiente JSON:[ { "firstName":"Alvaro", "surName":"Royo", "alias":"FreeWorld", "age":22, "height":1.8 }, { "firstName":"Nacho", "surName":"García", "age":26, "height":1.84 } ]Y crearemos un objeto que implemente a Codable:
import Foundation struct Person: Codable { var firstName: Stringvar surName: Stringvar alias: String? var age: Int var height: Double }Y podría ser mucho más complicado pero ya está. Ya tenemos un objeto Codable básico.
4. Inicializar el objeto Codable
Primero vamos a poner el JSON en el proyecto y accederemos a el desde el bundle para no complicarnos con servicios ETC. Como verás a continuación es sencillísimo.let url = Bundle.main.url(forResource: "persons", withExtension: "json") let fileData = try! Data(contentsOf: url!) let persons:[Person] = try! JSONDecoder().decode([Person].self, from: fileData)Y así quedaría:
5. ¿Backend nos modifica los nombres del JSON? ¡No pasa nada!
¿Que pasa si desde el back nos cambian los nombres de las propiedades en el JSON? Pues nada por que no tenemos más que añadir un simple enum dentro de la estructura del Codable y el solo lo cogerá. Vamos a cambiar firstName por first_name que a los de back les encantan las barras bajas. La única modificación que haremos en el objeto es incluir un enum que implemente a String y CodingKey de esta forma:enum CodingKeys: String, CodingKey { case firstName = "first_name" case surName case alias case age case height }Y segunda, es imprescindible incluir todas las variables aunque no tengan cambios ya que sino nos saltará un error de que no estamos implementando correctamente el protocolo. Listo, ya habríamos manejado un cambio de una propiedad en el JSON sin afectar para nada al resto del código.
6. Problemas con las fechas…
Yo creo que todos los desarrolladores hemos tenido problemas con las fechas, con los formatos etc… Bueno supongamos que nuestro JSON tiene una fecha que tiene un formato custom. ¿Como tratamos eso con los Codables? Bueno, es algo más complicado que lo anterior pero aún así es algo sencillo. Primero vamos a añadir una propiedad nueva a nuestra persona que será bornDate de tipo Date?. Tanto en el JSON como en el código. Recuerda que si tienes el enum para el cambio de nombre del JSON tienes que añadirlo también al enum. El formato en este caso será: «dd/MM/yyyy HH:mm» Vamos a hacer una extensión de DateFormatter y crearemos una constante estática que llamaremos bornDate y le pondremos la lógica de esta forma:extension DateFormatter { static let bornDate: DateFormatter = { let formatter = DateFormatter() formatter.dateFormat = "dd/MM/yyyy HH:mm" return formatter }() }El nuevo JSON seria:
[ { "first_name":"Alvaro", "surName":"Royo", "alias":"FreeWorld", "age":22, "height":1.8, "bornDate":"22/11/1995 15:43" }, { "first_name":"Nacho", "surName":"García", "age":26, "height":1.84 } ]Y el código quedaría así:
let url = Bundle.main.url(forResource: "persons", withExtension: "json") let fileData = try! Data(contentsOf: url!) let decoder = JSONDecoder() decoder.dateDecodingStrategy = .formatted(DateFormatter.bornDate) let persons:[Person] = try! decoder.decode([Person].self, from: fileData)Ya hemos visto la eficacia de los Codables, sin duda los usaré en los próximos proyectos ya que seguro que será dentro de poco un standard de programación en iOS y macOS y todas sus variantes.
Por: Álvaro Royo
iOS Senior Developer | Instructor de «Superpoderes iOS» en el Bootcamp Desarrollo Mobile de KeepCoding.
Si tienes algo que deseas compartir o quieres formar parte de KeepCoding, escríbenos a [email protected]