iOS. Acceso a la red
Acceso a la red en iOS
Al igual que hemos visto para Android, en esta sesión veremos cómo acceder a la red desde las aplicaciones iOS.
Conexión a URLs en iOS
Comenzaremos viendo cómo conectarnos con URLs.
En el API de Cocoa Touch encontramos diferentes objetos para representar las URLs y las conexiones a las mismas. Veremos que se trabaja con estos objetos de forma similar a las librerías de Java para conectarnos con URLs.
Para crear la conexión, antes de iOS7 teníamos la clase NSURLConnection
, que permitía hacer las conexiones de forma síncrona o asíncrona. Sin embargo, la gestión de estas conexiones dejaba bastantes aspectos del código al programador, lo que daba lugar a problemas de seguridad. Para simplificar el código, la mayoría de desarrolladores empezaron a usar clases alternativas de terceros como AFNetworking, hasta que Apple decidió añadir una clase integrada en iOS que hiciera algo parecido mejorando así la gestión de las conexiones. Esta clase, que es la que debe usarse siempre en iOS cuando trabajemos con el interfaz nativo, es URLSession
.
Podemos crear una nueva conexión del siguiente modo:
La configuración de la sesión puede ser de tres tipos:
default
: Usa una caché persistente en disco y guarda las credenciales en el keychain. Es la que se suele usar en la mayoría de casos.ephemeral
: No se guarda ningún dato en disco. Toda la caché, credenciales y demás datos se guardan en la RAM y se vinculan a la sesión. Cuando la sesión termina, estos datos se eliminan.background
: Son similares a las sesionesdefault
, pero usan un proceso separado para gestionar toda la transferencia de datos. Se emplea cuando queremos descargar datos mientras la aplicación está suspendida.
Como alternativa, si necesitamos algo sencillo en iOS podemos usar también una sesión singleton que está creada por defecto y que se llama shared:
La sesión compartida puede usarse desde cualquier punto de nuestra aplicación, y es interesante cuando nuestras peticiones son sencillas y no requieren cambios en la configuración, ya que la sesión compartida no puede modificarse.
Una vez tenemos creada la sesión podemos realizar una tarea mediante una petición. Para ello primero tenemos que crear un objeto de la clase URL
que representa y que se inicializa con la cadena de la dirección correspondiente:
Podemos usar directamente la URL, pero en general es conveniente filtrar la cadena por si contiene caracteres no permitidos:
Para hacer una petición a dicha URL, deberemos crear un objeto URLRequest
a partir de la misma. En la creación de la petición podemos especificar también la política de caché a seguir, o el timeout de la conexión.
Si queremos modificar más datos de la petición, deberíamos utilizar un objeto NSMutableURLRequest
en su lugar. En nuestra petición podemos añadir valores como el tipo de método (GET,POST,etc.), como veremos en la parte de servicios REST.
Una vez tenemos la petición y la sesión configurada, podemos lanzar la consulta mediante una tarea:
Este método lanza una petición asíncrona y recibe la respuesta del servidor, los datos, y un error si se ha producido algún problema. Además de dataTask(with:request)
, podemos seleccionar otros tipos de tareas:
dataTask(with:request)
,dataTask(with:url)
: Se usan para peticiones puntuales al servidor.dataTask(with: url)
es similar adataTask(with:request)
pero no necesita unaURLRequest
sino sólo unaURL
, y por tanto puede usarse para conexiones sencillas que no requieran cabeceras o configuraciones específicas.downloadTask(with:request)
,downloadTask(with:url)
: Descargan datos en forma de fichero y soportan descargas en segundo plano cuando la aplicación no se está ejecutando. Se usan por ejemplo cuando queremos descargar una serie de imágenes y mostrarlas en una tabla.uploadTask(with:request)
,uploadTask(with:url)
: Como la anterior, pero para subir ficheros al servidor en lugar de descargarlos.
Vamos a ver un ejemplo completo de código, con una clase propia Connection
que usaremos para gestionar las conexiones. Esta clase asumirá que la sesión y la petición están ya creadas. Implementar esto en una clase separada nos va a permitir que nuestra app pueda crear conexiones desde distintos controladores de forma más limpia. El fichero Connection.swift
contiene lo siguiente:
Con esta clase podemos crear una conexión básica desde cualquier punto de nuestra aplicación. Por ejemplo:
Apple Transport Security
Muy importante: Desde iOS9, Apple obliga a adoptar el protocolo ATS (Apple Transport Security) para incrementar la seguridad de las conexiones. Debido a esto, por defecto no podemos conectarnos a ninguna web sin usar SSL o TLS, por lo que por ejemplo no podríamos hacer una conexión directa para descargar una imagen de cualquier web, o tampoco podemos acceder a la de la UA. Para evitar esto, hay que añadir una excepción en nuestro fichero plist
abriéndolo como un fichero de texto:
Alternativamente podemos también añadir la excepción desde el visor de XCode:
Esto sólo es necesario si nuestra aplicación permite la conexión a cualquier servidor y no tenemos control sobre los servidores externos. Si sólo nos conectamos un servidor y este no tiene TLS, podemos añadir una excepción sólo para él mediante NSExceptionDomains
. Por ejemplo:
Indicador de actividad de red
Mientras estamos descargando datos debemos mostrar el indicador actividad de red en la barra de estado. Esto no se hace de forma automática, por lo que debemos añadir la siguiente instrucción, que debe ejecutarse en el hilo principal:
Evidentemente, cuando las descargas finalicen debemos ocultar el indicador asignándole el valor false
.
Comprobación de la conectividad en iOS
En iOS también podemos comprobar el tipo de conexión con el que contamos, aunque la comprobación no es tan sencilla como en Android. Uno de los ejemplos publicados en la documentación de Apple consiste precisamente en esto, se llama Reachability y se puede encontrar en la sección Sample Code de la ayuda. Lo más habitual entre los desarrolladores hasta hace poco era incorporar el código del ejemplo Reachability, pero no está actualizado para Swift.
Carga lazy de imágenes en iOS
Para hacer carga lazy de imágenes conatmos con un ejemplo proporcionado por Apple en la documentación de iOS, pero es demasiado complejo y se puede hacer lo mismo con unas pocas líneas, así que vamos a ver esta solución simplificada.
Para descargar una imagen y mostrarla en la celda de en una tabla debemos editar el método cellForRowAt
para actualizar cada celda con su imagen correspondiente descargada en segundo plano. Por ejemplo:
Primero inicializamos la celda con una imagen que se mostrará mientras que no termine la descarga, y usamos el método dataTask
para lanzar la petición. Cuando se reciba la respuesta del servidor la tendremos en el bloque completionHandler
. Si no hay ningún error, se han recibido datos y además el código de respuesta es 200, entonces esperamos a que termine la descarga (con DispatchQueue.main.async
) y guardamos los datos en la imagen de la celda. Este método es simple, eficiente y todo se hace en segundo plano. Añadiendo este código al método cellForRowAt
se descargarán sólo las imágenes correspondientes a las celdas de la vista actual de la tabla.
Descargar y subir archivos por red
Cuando queremos hacer una descarga o subir un archivo (por ejemplo, un documento), es recomendable que usemos downloadTask
o uploadTask
en lugar de dataTask
. La ventaja sobre dataTask
es que así podemos retomar la descarga o la subida si se produce alguna interrupción o si se supera el tiempo de espera. Podemos inicializar una conexión con downloadTask
del siguiente modo:
Necesitaremos también adoptar el protocolo URLSessionDownloadDelegate
, y cuando el fichero se descargue se invocará el siguiente método, que podemos editar para lo que queramos hacer:
Como puede verse en este código, downloadTask
no devuelve los datos sino una url con el contenido que se almacena localmente en el móvil. En el ejemplo, esta dirección se guarda en location
. Una vez tenemos la localización podemos guardar permanentemente el fichero en el sistema de archivos (FileManager
) con el nombre que escojamos. El código anterior necesita que implementemos la función localFilePathForUrl
para obtener la ruta local del fichero que queremos guardar:
Ejercicios de acceso a la red en iOS
Visor de HTML (1.5 puntos)
Vamos a hacer una aplicación que nos permita visualizar el código HTML de la URL que indiquemos. En las plantillas de la sesión encontramos el proyecto LectorHtml
que podemos utilizar como base. Este proyecto contiene una pantalla con un cuadro de edición texto para introducir la URL, un botón para cargar la URL, y un visor de texto donde deberemos mostrar los resultados obtenidos cuando se pulse el botón. Se pide:
a) Implementar el código necesario para que cuando se pulse el botón se realice una conexión a la URL indicada, se obtenga el resultado como texto, y se muestre en el visor de texto. Para ello puedes añadir la clase Connection
que hemos visto anteriormente.
Ayuda: Trabajaremos con la clase
ViewController
. Concretamente la conexión deberá ser iniciada en el métodoconnectUrl
.
b) Desactivar ATS en el fichero info.plist
para poder conectarnos a cualquier servidor.
c) Mostrar en la barra de estado un indicador de actividad de red mientras se descarga el contenido. Haz también que el botón se deshabilite mientras dure la descarga.
Carga de imágenes (1.5 puntos)
Vamos a implementar una aplicación que nos permita visualizar una lista de imágenes de carátulas de películas, y que al pulsar sobre cada una de ellas nos aparezca la información de la película.
Se proporciona una plantilla Pelis
que utilizaremos como base. En esta sesión no usaremos servicios REST, ya que esto lo veremos en la siguiente. Todos los datos de las películas estarán almacenados en nuestro programa, y sólo accederemos a la red para descargar las imágenes del servidor.
Primero actualizaremos las imágenes de la tabla de forma lazy, es decir, cargándolas conforme se solicita que se muestren las celdas en pantalla.
Una vez implementada la carga lazy, asigna la imagen correspondiente en la vista detalle para que se muestre cuando se pulse sobre ella desde la tabla maestra.
Tras actualizar una celda con una nueva imagen descargada, imprime (con print
) el título de la película. Si ejecutas el programa verás que la misma imagen se descarga varias veces cuando hacemos scroll. Para evitar esto, cuando se descargue una imagen almacénala en el campo image
de la película correspondiente del array, y en cellForRowAt
haz la descarga sólo si la imagen de la película no se había descargado antes. Para comprobar que has hecho bien esto, añade el siguiente código dentro del bloque de la cola principal (DispatchQueue.main
):
Last updated