Вы находитесь на странице: 1из 29

Desarrollo de aplicaciones móviles con iOS

Networking

PhD. Francisco Martínez


fomarti@unicauca.edu.co

PhD. Francisco Martínez


Web Services

Maps
Cloud computing
Analytics
Advertising

Ver: https://www.programmableweb.com/

PhD. Francisco Martínez


Bookcase App – Consulta libros en línea
https://developers.google.com/books/

Consulta de
información del libro
a través de ISBN
scan

PhD. Francisco Martínez


BookCase App – Consulta online de libros
1. Diseño del código de networking. Cuando el BookViewController recibe el
código de barras, lo envía al Web Service para consultar información del libro.
Se definirá una nueva clase en el modelo para manejar el código de conexión
al WS y retornar la información al BookViewController, quien a su vez
actualizará los Views. Esto mantiene el código organizado y reusable.

PhD. Francisco Martínez


BookCase App – Consulta online de libros
2. Crear la clase Google Books Service en el modelo. Cree un archivo Swift llamado
“GoogleBooksService”. Este archivo contendrá la lógica para conectarse al WS. Defina
un método “getBook” en el BookViewController para comunicarse con esta clase y
obtener la información del libro; como la operación se realizará en thread separado, es
necesario pasar un closure que reciba los datos del servidor. Defina un método cancel,
para cancelar la operación.

GoogleBookService.swift
class GoogleBooksService {
}

Invocado desde una


BookViewController.swift operación asíncrona

func getBook(with barcode: String,


completionHandler: @escaping (Book?, Error?) -> Void) {
// Get book from web service
}

func cancel() {
// Cancel any web service operations
}

PhD. Francisco Martínez


BookCase App – Consulta online de libros
3. Defina un protocolo BooksService. Defina el protocolo “BooksService” en el
archivo GoogleBooksService. Esto permite independizar la implementación del
servicio a los ViewControllers, en caso que este expire o se modifique en el
futuro.

protocol BooksService {
func getBook(with barcode: String,
completionHandler: @escaping (Book?, Error?) -> Void)
func cancel()
}

class GoogleBooksService: BooksService {


}

PhD. Francisco Martínez


BookCase App – Consulta online de libros
4. Definición de las variables en el ViewController. Instancie un objeto del
GoogleBooksService en el BookViewController; defínala conforme al protocolo, para
manejar posibles cambios en el futuro. Invoque el método getBook en el método
foundBarcode para realizar la consulta cuando el código de barras sea detectado. La
comunicación con el BookViewController está completa!

var booksService: BooksService = GoogleBooksService()

Método foundBarcode
booksService.getBook(with: barcode) {
(scannedBook, error) in
if error != nil {
// Deal with error here
return
} else if let scannedBook = scannedBook {
self.titleTextField.text = scannedBook.title
self.authorTextField.text = scannedBook.author
self.bookCover.image = scannedBook.cover
self.isbnTextField.text = barcode
}else {
// Deal with no error, no book!
}
}

PhD. Francisco Martínez


Componentes de la comunicación con el Web
Service
Para realizar la comunicación con el Web Service se requiere seguir los
siguientes pasos:

1. Un objeto URLSession (configurado opcionalmente a través de un


objeto URLSessionConfiguration).
2. Un objeto URL (opcionalmente usando URLComponents con
propósitos de personalización).
3. Usar los objetos URLSession y URL para crear una tarea (task).
4. Iniciar (o reinicie) la tarea.
5. Recibir las respuestas del WebService ya sea a través de un
callback o métodos delegados.

PhD. Francisco Martínez


Componentes de la comunicación con el Web
Service

PhD. Francisco Martínez


Componentes de la comunicación con el Web
Service
Configuración del objeto URLSession (a través de URLSessionConfiguration):

Configuración Ejemplo
• Default: Almacena ls respuestas en disco (caché) let configuration =
• Ephemeral: No realiza caché URLSessionConfiguration.default
• Background: permite que las tareas sean realizadas
cuando el app está en background
• requestCachePolicy: determina cuándo las solicitudes configuration.requestCachePolicy =
en esta sesión chequean datos del caché. .reloadIgnoringLocalCacheData
(Los cachés son ignorados)
• timeoutIntervalForRequest: tiempo aceptable antes configuration.timeoutIntervalForRequest =
que una solicitud expire. 30
(por default, 60)
• allowsCellularAccess: especifica si la sesión debería configuration.allowsCellularAccess = false
usar redes celulares. (previene acceso a redes celulares)

PhD. Francisco Martínez


Componentes de la comunicación con el Web
Service
Formas de acceder al objeto URLSession :

Configuración Ejemplo
• Shared session: apropiado para tareas básicas de red. let session = URLSession.shared
No puede ser personalizada más allá de la
configuración por defecto.
• Instancia a través de un objeto de configuración de let session = URLSession(configuration:
sesión. configuration)

• Instancia a través de un objeto de configuración de let session = URLSession(configuration:


sesión, delegate y queue. configuration, delegate: self,
delegateQueue: OperationQueue.main)

En adición al objeto de configuración, se puede especificar un delegate para recibir


notificaciones adicionales de la sesión y permitir configuración adicional. Dado que
este tipo de operaciones se hacen en un background thread es conveniente definir un
queue para atender las solicitudes del servidor (con propósitos de actualización de la
interfaz).

PhD. Francisco Martínez


BookCase App – Consulta online de libros
5. Crear un objeto URLSession. Agregue un objeto URLSession como una
propiedad de la clase GoogleBooksService. Fije ésta como el delegado de la
sesión y especifique que las respuestas serán enviadas al queue principal. Para
esto actualice la definición de la clase.

class GoogleBooksService: NSObject, BooksService, URLSessionDelegate {

lazy var session: URLSession = {


let configuration = URLSessionConfiguration.default
return URLSession(configuration: configuration,
delegate: self, delegateQueue:
OperationQueue.main)
}()

PhD. Francisco Martínez


BookCase App – Consulta online de libros
6. Crear un objeto URL. El servicio de Google se encuentra alojado en
https://developers.google.com/books/docs/v1/using#WorkingVolumes. Use la
siguiente cadena como prueba del servicio:
https://www.googleapis.com/books/v1/volumes?q=9780767926034. Defina una
constante en la clase GoogleBooksService como un String que contenga esta URL
(excepto la cadena la solicitud específica). Cree un objeto URL a partir de un objeto
URLComponents en el método getBook.

let googleUrl = "https://www.googleapis.com/books/v1/volumes"

func getBook(with barcode: String,


completionHandler: @escaping (Book?, Error?) -> Void){
var components = URLComponents(string: googleUrl)!
components.queryItems = [
URLQueryItem(name: "q", value: barcode)]
guard let url = components.url else {return}
print(url.query!) // q=9781617294075 for example
}

PhD. Francisco Martínez


BookCase App – Consulta online de libros
7. Crear un objeto URLRequest. Cree un objeto URLRequest a partir del objeto
URL (método getBook). A través de este objeto se pueden realizar
configuraciones como las siguientes:

Configuración Descripción

cachePolicy Determina si la solicitud chequea datos en el caché

timeOutInterval Tiempo de timeout

HTTPMethod GET (default) o POST

networkServiceType Especifica tipos de datos para priorización de tráfico


(Ej: default, voip, video, background y voice)

//generate request
let request = URLRequest(url: url)

PhD. Francisco Martínez


BookCase App – Consulta online de libros
8. Crear las tareas para invocar el Web Service. A través del objeto URLSession, se
crearán y coordinarán las tareas necesarias para invocar el Web Service. Un task puede
ser de 3 tipos (ver tabla); use un dataTask al crear un objeto URLSessionDataTask. Un
completion handler manejará la respuestas desde el servidor, la cual contiene los datos
y posibles objetos para manejo de errores.
Tipo de task Descripción
Data Solicitar pequeñas cantidades de datos (texto)
Download Grandes cantidades de datos
Upload Subir datos (Ej. Un archivo)

let dataTask = session.dataTask(with: request) {


(data, response, error) in
if let error = error {
completionHandler(nil, error)
}
guard let data = data else { return }
// Get book information
}
dataTask.resume()

PhD. Francisco Martínez


BookCase App – Consulta online de libros
9. Examinar la estructura de los datos. La respuesta desde el servicio se entrega en
formato JSON. Use el siguiente visor en línea, para comprender mejor la estructura de
la información que se va a parsear: http://jsonviewer.stack.hu/. Use la siguiente URL de
ejemplo: https://www.googleapis.com/books/v1/volumes?q=9780767926034

PhD. Francisco Martínez


BookCase App – Consulta online de libros
10. Parsear la información de la respuesta del Web Service. Use un objeto jsonObject
de la clase JSONSerialization, el cual serializa el objeto JSON en tipos de datos
Foundation. Defina un método parseJSON en la clase GoogleBooksService que recibe
un completion handler para retornar el resultado.

private func parseJSON(data: Data,


completionHandler:
@escaping (Book?, Error?) -> Void) {
do {
if let dataAsJSON =
try JSONSerialization.jsonObject(
with: data,
options: [])
as? [String: Any] {
// -------> Traverse hierarchy
} else {
completionHandler(nil, nil)
}
} catch let error as NSError {
completionHandler(nil, error)
return
}
}

PhD. Francisco Martínez


BookCase App – Consulta online de libros
10b. Parsear la información de la respuesta del Web Service. Recorrer la jerarquía del
objeto JSON para extractar los datos requeridos. Reemplace el código de la cláusula do
del método parseJSON. Finalmente, genere un objeto de tipo Book con esta
información. Actualice el método getBook para que invoque el método parseJSON.
Método parseJSON
if let dataAsJSON = try JSONSerialization.jsonObject(with: data, options: []) as?
[String:Any],
let items = dataAsJSON["items"] as? [Any],
let volume = items[0] as? [String:Any],
let volumeInfo = volume["volumeInfo"] as? [String:Any],
let title = volumeInfo["title"] as? String,
let authors = volumeInfo["authors"] as? [String] {
let book = Book(title: title,
author: authors.joined(separator: ","),
rating: 0, isbn: "0", notes: "")
completionHandler(book,nil)

Método getBook
// Get book information
self.parseJSON(data: data, completionHandler: completionHandler)

PhD. Francisco Martínez


BookCase App – Consulta online de libros
11. Ejecute la aplicación. Ejecute la aplicación y seleccione la opción para agregar un
libro. Escanee un código de barras que contenga el ISBN de un libro y observe cómo la
información se recupera directamente desde el servicio Web de Google Books!

PhD. Francisco Martínez


BookCase App – Consulta online de libros
Descarga de datos desde un servicio Web. Qué tal si mejoramos nuestro App
para que descargue la imagen de la portada del libro a partir de la información
entregada por el Web Service?. Exploremos la utilización de un Download
Task.

PhD. Francisco Martínez


BookCase App – Consulta online de libros
1. Revisar la estructura JSON de la respuesta para extractar la URL de la
imagen. En la estructura JSON de la respuesta, observe que es posible
extractar una URL para obtener la imagen de la portada del libro en el campo
thumbnail.

PhD. Francisco Martínez


BookCase App – Consulta online de libros
2. Parsear la estructura JSON del objeto que contiene el link para extractar la
imagen. En la jerarquía del documento JSON extraer el objeto thumbnail
disponible en la estructura imageLinks. Agregue las últimas líneas a la rutina
del método parseJSON de la clase GoogleBooksService.

if let dataAsJSON = try JSONSerialization.jsonObject(with: data, options:


[]) as? [String:Any],
let items = dataAsJSON["items"] as? [Any],
let volume = items[0] as? [String:Any],
let volumeInfo = volume["volumeInfo"] as? [String:Any],
let title = volumeInfo["title"] as? String,
let authors = volumeInfo["authors"] as? [String],
let imageLinks = volumeInfo["imageLinks"] as?
[String:Any],
let thumbnailURL = imageLinks["thumbnail"] as? String {

PhD. Francisco Martínez


BookCase App – Consulta online de libros
4. Definir la comunicación con el Download task que descargará la imagen.
Al finalizar el parseo de la información, invoque un método loadCover que no
sólo invoca el completionHandler sino que además entrega la información del
libro y la cadena con la URL para descargar la imagen. Modificar la invocación
al completionHandler en el método parseJSON de la clase GoogleBooksService
por las siguientes líneas:

let book = Book(title: title,


author: authors.joined(separator: ","),
rating: 0, isbn: "0", notes: "")
loadCover(book: book,
thumbnailURL: thumbnailURL,
completionHandler: completionHandler)

PhD. Francisco Martínez


BookCase App – Consulta online de libros
5. Crear el Download task para descargar la información de la imagen.
Adicione el método loadCover a la clase GoogleBooksService y en él defina un
downloadTask a partir del objeto URLSession para conectarse a la URL de la
imagen. Cree un objeto UIImage (se requiere importar UIKit) a partir de los
datos obtenidos y actualice la propiedad cover del objeto Book.
func loadCover(book:Book,thumbnailURL:String, completionHandler: @escaping (Book?,
Error?) -> Void) {
var book = book
guard let url = URL(string: thumbnailURL) else {return}
let task = session.downloadTask(with: url) { (tempURL, response, error) in
if let tempURL = tempURL,
let data = try? Data(contentsOf: tempURL),
let image = UIImage(data: data) {
book.cover = image
}
completionHandler(book,error)
}
task.resume()
}

PhD. Francisco Martínez


BookCase App – Consulta online de libros
6. Ejecute la aplicación. Observe que la aplicación se conecta correctamente al servicio
pero la portada del libro no es actualizada y se observa en consola un error de
seguridad. Por defecto, las apps en iOS tienen bloqueado el acceso a dominios no
seguros (aquellos que no usan HTTPS). Es necesario configurar excepciones en el
archivo info.plist para acceder a dominios considerados como no seguros.

PhD. Francisco Martínez


BookCase App – Consulta online de libros
7. Agregar las excepciones para conectarse a dominios no seguros. En tiempo de
desarrollo, puede ser suficiente con fijar la propiedad NSAllowsArbitraryLoads en true.
No obstante, en producción es conveniente fijar excepciones para los dominios
específicos para mantener nuestra app segura. Haga click derecho sobre info.plist y
seleccione Open as – Source code; inserte las siguientes líneas:

<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>books.google.com</key>
<dict><key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>

PhD. Francisco Martínez


BookCase App – Consulta online de libros
8. Desplegar un indicador de actividad de red. Si la tarea en red puede tardar más de
un par de segundos, es una buena idea incluir un indicador de actividad de red en la
barra de estado. Fije la propiedad isNetworkActivityIndicatorVisible del objeto
UIApplication en true antes de iniciar la operación y luego asigne el valor de false
cuando ésta culmine en el método foundBarcode del BookViewController.

func foundBarcode(barcode:String) {

UIApplication.shared.isNetworkActivityIndicatorVisible = true
booksService.getBook(with: barcode) {
(scannedBook, error) in
UIApplication.shared.isNetworkActivityIndicatorVisible =
false
if error != nil {

PhD. Francisco Martínez


BookCase App – Consulta online de libros
9. Ejecute la aplicación. Ejecute la aplicación y observe cómo se carga
satisfactoriamente la información del libro incluyendo su portada, una vez se realiza la
lectura del código de barras.

PhD. Francisco Martínez


¡Gracias!
¿Preguntas?

PhD. Francisco Martínez


fomarti@unicauca.edu.co

PhD. Francisco Martínez

Вам также может понравиться