iOS. Servicios Rest
Last updated
Last updated
En la primera parte de esta sesión veremos cómo parsear la información recibida de un servidor, tanto en JSON como en XML.
El parsing de JSON no se incorporó al SDK de iOS hasta la versión 5.0. Anteriormente contábamos con diferentes librerías que podíamos incluir para realizar esta tarea, como () o (). Sin embargo, actualmente podemos trabajar con JSON directamente con las clases de Cocoa Touch sin necesidad de incluir ninguna librería adicional.
Para esto simplemente necesitaremos la clase JSONSerialization
. A partir de ella obtendremos el contenido del JSON en una jerarquía de objetos. El método jsonObject
de la clase JSONSerialization
nos devolverá un diccionario o un array según si el elemento principal del JSON es un objeto o una lista, respectivamente.
A veces la información JSON está en forma de diccionario (el elemento principal del JSON es un objeto), y otras organizada como un array (el elemento principal es una lista). Vamos a ver un ejemplo cuando el elemento principal es un objeto:
En este caso, nuestro código podría ser el siguiente:
Si en su lugar el elemento principal del JSON fuera un array:
Podríamos procesarlo del siguiente modo:
El objeto JSONSerialization
también nos permite realizar la transformación en el sentido inverso, permitiendo transformar una jerarquía de objetos Array
y Dictionary
en una representación JSON. Para eso contaremos con el método jsonObject
:
Parsing JSON con MVC
Hemos visto la forma básica de serializar o deserializar datos en JSON. Sin embargo, nuestras apps suelen seguir el patrón de diseño MVC, por lo que normalmente es más limpio y conveniente convertir directamente los objetos JSON al formato de nuestro modelo. Vamos a ver un ejemplo de cómo se haría la deserialización. Dado el siguiente modelo:
Y el siguiente documento JSON:
podemos crear un constructor para nuestro modelo a partir de un diccionario:
Y la llamada a nuestro constructor sería:
Parsing JSON con métodos Codable
Swift4 permite serializar clases, registros (struct) o tipos enumerados (enum) para leer o escribir en JSON. Para codificar o decodificar un tipo personalizado podemos usar la opción Encodable
, Decodable
o Codable
, que permiten tanto codificación como decodificación JSON como puede verse en el siguiente ejemplo:
Aunque esto suele ser suficiente para la mayoría de casos, a veces podemos querer omitir algunas variables en el proceso de serialización, o poner nombres a nuestras variables que no coinciden exactamente con los del JSON. Para resolver estas dos cuestiones, swift introdujo las CodingKeys
que podemos ver en el siguiente ejemplo:
En el SDK de iOS contamos con la clase XMLParser
para analizar XML. Con esta librería el análisis se realiza de forma parecida a los parsers SAX de Java. Este es el parser principal incluido en el SDK, aunque también contamos dentro del SDK con libxml2
, escrito en C, que incluye tanto un parser SAX como DOM. Además encontramos otras librerías que podemos incluir en nuestro proyecto como parsers DOM de XML:
Parser
URL
TBXML
TouchXML
KissXML
TinyXML
GDataXML
Nos vamos a centrar en el estudio de XMLParser
por ser el parser principal incluido en la API de Cocoa Touch.
Para implementar un parser con esta librería deberemos crear una clase que adopte el protocolo XMLParserDelegate
. Este define, entre otros, los siguientes métodos:
Podemos observar que nos informa de tres tipos de eventos: didStartElement
, didEndElement
y foundCharacters
. El análisis del XML será secuencial, es decir, el parser irá leyendo el documento y nos irá notificando los elementos que encuentre. Cuando se abra una etiqueta, llamará al método didStartElement
de nuestro parser, cuando encuentre texto llamará a foundCharacters
, y cuando se cierra la etiqueta llamará a didEndElement
. Será responsabilidad nuestra implementar de forma correcta estos tres eventos, y guardar la información de estado que necesitemos durante el análisis.
Por ejemplo, imaginemos un documento XML sencillo como el siguiente:
Podemos analizarlo mediante un parser XMLParser
como el siguiente:
Podemos observar que cada vez que encuentra una etiqueta de apertura obtenemos tanto la etiqueta como sus atributos. Cada vez que se abre un nuevo mensaje se van introduciendo en el objeto de tipo UAMensaje
los datos que se encuentran en el XML, hasta encontrar la etiqueta de cierre (en nuestro caso el texto, aunque podríamos tener etiquetas anidadas).
Para que se ejecute el parser que hemos implementado mediante el delegado deberemos crear un objeto XMLParser
y proporcionarle dicho delegado (en el siguiente ejemplo suponemos que nuestro objeto self
hace de delegado). El parser se debe inicializar proporcionando el contenido XML a analizar (encapsulado en un objeto Data
):
Tras inicializar el parser, lo ejecutamos llamando al método parse
, que realizará el análisis de forma síncrona, y nos devolverá true
si todo ha ido bien, o false
si ha habido algún error al procesar la información. También devolverá false
si durante el parsing llamamos al método parser.abortParsing()
.
En iOS podemos acceder a servicios REST utilizando las clases para conectar con URLs vistas en anteriores sesiones. Por ejemplo, para hacer una consulta al servidor de OpenWeatherMap podríamos utilizar el siguiente código para iniciar la conexión (recordemos que este método de conexión es asíncrono):
Podemos modificar los datos de la petición y de esta forma establecer todos los datos necesarios para la petición al servicio: método HTTP, mensaje a enviar (como XML o JSON), y cabeceras (para indicar el tipo de contenido enviado, o los tipos de representaciones que aceptamos). Por ejemplo:
Podemos ver que en la petición POST hemos establecido todos los datos necesarios. Por un lado su bloque de contenido, con los datos del recurso que queremos añadir en la representación que consideremos adecuada. En este caso suponemos que utilizamos XML como representación. En tal caso hay que avisar de que el contenido lo enviamos con este formato, mediante la cabecera Content-Type
, y de que la respuesta también queremos obtenerla en XML, mediante la cabecera Accept
.
Se proporciona una plantilla Weather
que ya realiza la llamada asíncrona a la API. Según el usuario elija XML o JSON, la respuesta del servidor se recibirá en el formato correspondiente. Crea una nueva conexión y lánzala en el método search
.
Se pide parsear la respuesta en ambos formatos para poder mostrar la información en pantalla. Sólo se solicitan unos pocos datos, que son la temperatura actual, la humedad, velocidad del viento, el país, y la descripción (que es un mensaje, como por ejemplo clear skies).
Hay que completar los métodos parseXML
y parseJSON
para mostrar la información correspondiente en los outlets del interfaz. Es recomendable comenzar con parseJSON
, completando el método init
de la clase Weather
. Para parsear el XML hace falta añadir al final del ViewController
los métodos didStartElement
, didEndElement
y foundCharacters
.
Puedes encontrar más ejemplos de cómo trabajar con JSON en .
Puedes encontrar más información sobre Codable
en .
En este ejercicio vamos a practicar el parsing de XML y JSON. Para ello haremos una aplicación que nos permita visualizar el tiempo de una ciudad accediendo a la API de .
Cuando hayas terminado de implementar estos métodos, descarga la imagen del icono que se encuentra en el campo icon
de la respuesta para mostrarlo en el UIImageView
, dentro del método updateView
. Ejemplo de la URL correspondiente al icono 10d
: .