Interacción con el servidor a través de la API en iOS en Swift 3. Parte 2

Este artículo es una continuación del artículo Interactuar con el servidor a través de una API en iOS en Swift 3. Parte 1 y actualizar un artículo antiguo escrito en Objective-C en swift 3.

Breve teoría


OBTENER solicitud


GET es el método de solicitud HTTP más simple, y es lo que usa el navegador para cargar páginas web. Se utiliza para solicitar contenido ubicado en una URL específica. El contenido puede ser, por ejemplo, una página web, una imagen o un archivo de audio. Por convención, las solicitudes GET son de solo lectura y, de acuerdo con el estándar W3C, no deben usarse en operaciones que cambien el lado del servidor. Por ejemplo, no usaremos una solicitud GET para enviar un formulario o una foto, porque estas operaciones requieren algunos cambios en el lado del servidor (usaremos POST en estos casos).

Solicitud POST


POST envía datos para su posterior procesamiento a la URL. Los parámetros se incluyen en el cuerpo de la solicitud con el mismo formato que GET. Por ejemplo, si queremos publicar un formulario que contenga dos campos, un nombre y una edad, enviaremos algo similar a name = Martin & age = 29 en el cuerpo de la solicitud.

Este método de envío de parámetros es ampliamente utilizado en páginas web. Los casos más populares son las formas. Cuando completemos el formulario en el sitio y haga clic en Enviar, lo más probable es que la solicitud sea POST.

En nuestra aplicación, usamos POST para clasificar los chistes. Enviaremos votos (o +1, o -1) al servidor remoto.

Los datos de solicitud POST pueden estructurarse utilizando diferentes formatos. Los parámetros generalmente se formatean de acuerdo con los estándares de codificación form-url (de acuerdo con el estándar HTML W3C). Este es el formato predeterminado y se usa ampliamente en muchos navegadores. Nuestro método acepta el diccionario Diccionario como argumento, pero no podemos enviar el diccionario Diccionario a través de la conexión HTTP porque es un tipo Swift interno. Para reenviar una conexión HTTP, necesitamos crear una representación reconocible del diccionario. Es como comunicarse con un extranjero. Traducimos nuestro mensaje a un idioma universal, y se traduce de un idioma universal a su idioma nativo. El lenguaje universal en HTTP es el estándar W3C, nuestro idioma es Swift, el idioma del destinatario es desconocido para nosotros.

El estándar W3C define reglas que definen lo que significa una representación reconocible para cada caso. En nuestro caso, necesitamos presentar los parámetros siguiendo la parte del estándar codificada en formato url (por ejemplo, param1 = var1 & param2 = var2).

Webview


Usando herramientas de lenguaje de programación, podemos enviar solicitudes a un servidor remoto. Esto es lo que hacen los navegadores antes de mostrar una página web. La diferencia está solo en el contenido de la respuesta. Las páginas web están formateadas utilizando el estándar HTML, que define un conjunto de reglas sobre cómo definir gráficamente varias etiquetas de marcado. Estas reglas parecen simples, pero mostrar una página completa siguiendo el estándar W3C es una tarea difícil. Afortunadamente, iOS tiene un componente UIWebView incorporado que utiliza el conocido motor WebKit e interpreta HTML / CSS / JavaScript y muestra páginas web completas dentro de una UIView.

Hay varios casos en los que queremos controlar el flujo de navegación. Por ejemplo, queremos saber cuándo se carga cierto contenido o una URL específica.

O tal vez estamos implementando un navegador seguro para niños, queremos impedir que el usuario cargue páginas que caen bajo ciertos criterios, como sexo o drogas. Para todos estos tipos de personalización, creamos una instancia de una clase que implementa el protocolo UIWebViewDelegate como un delegado UIWebView. Podemos implementar los siguientes métodos:

webView:shouldStartLoadWithRequest:navigationType: webViewDidStartLoad: webViewDidFinishLoad: webView:didFailLoadWithError: 

Usando el primer método, podemos controlar el flujo de navegación al permitir o bloquear solicitudes específicas. Los otros tres métodos son eventos informativos (los nombres de los métodos dan una buena idea del evento).

Implementación de votación por solicitud POST


Agregaremos los archivos MainViewController.swift y HTTPCommunication.swift , y crearemos un nuevo archivo WebViewController.swift en el que definiremos nuestro webView.

Primero, cree un nuevo archivo WebViewController.swift y defina la clase WebViewController en él:

WebViewController.swift

 import UIKit class WebViewController: UIViewController { } extension WebViewController { } 

Ahora implementamos la funcionalidad para realizar solicitudes POST en la clase responsable de todas nuestras operaciones HTTP: la clase HTTPCommunication. Para hacer esto, agregue un nuevo método postURL (_: params: completeHandler :) en HTTPCommunication.swift, que es similar al método anterior retrieveURL (_: completarHandler :).

HTTPCommunication.swift

 class HTTPCommunication: NSObject { … func retrieveURL(_ url: URL, completionHandler: @escaping ((Data) -> Void)) { … } // TODO:    } // end of class HTTPCommunication 

Código de método

 func postURL(_ url: URL, params: [String: Int], completionHandler: @escaping ((Data) -> Void)) { // (K.1) self.completionHandler = completionHandler // (K.2) func postBody(params: [String: Int]) -> Data? { // (K.3) var paramsArr: [String] = [] for (key, value) in params { paramsArr.append("\(key)=\(value)") } let postBodyString: String = paramsArr.joined(separator: "&") let postBodyData: Data? = postBodyString.data(using: .utf8) return postBodyData } var request: URLRequest = URLRequest(url: url) // (K.4) request.httpMethod = "POST" request.httpBody = postBody(params: params) let session: URLSession = URLSession(configuration: .default, delegate: self, delegateQueue: nil) // (K.5) let task: URLSessionDownloadTask = session.downloadTask(with: request) task.resume() } 

(K.1) Con el cierre, trabajaremos fuera de esta función, por lo tanto, lo denotamos por @escaping.
(K.2) Almacenamos el cierre aprobado en la propiedad completeHandler.
(K.3) Escribimos un método que convierte el diccionario de parámetros pasados ​​en forma de cadena y luego codifica esta cadena en Tipo de datos en codificación utf8.
(K.4) Creamos una solicitud con la url pasada y la configuramos: establecemos POST como método y hacemos nuestros parámetros del tipo de datos con utf8 como cuerpo de solicitud.
(K.5) Finalmente, cree una sesión con una configuración predeterminada y un delegado como nosotros mismos. Y creamos una tarea para cargar el resultado de esta solicitud en el almacenamiento temporal. Al final, descongela la tarea.

Ahora agreguemos la interfaz necesaria a MainViewController.swift. Agregue dos botones para votar voteUp y voteDown, que aumentarán o disminuirán respectivamente la calificación del chiste actual. También agregamos el botón "Chuck Who?", Que llenaremos con funcionalidad en la sección de vista web.

En MainViewController.swift, inserte el código de inicialización voteUpButton, voteDownButton y chuckWhoButton.

MainViewController.swift

 lazy var jokeLabel: UILabel! = … var jokeID: Int! lazy var activityView: UIActivityIndicatorView! = … //TODO:    lazy var stackView: UIStackView! = … 

Código de inicialización del botón
 lazy var voteUpButton: UIButton! = { // (L.1) let button: UIButton = UIButton(type: .system) // (L.2) button.setTitle("Vote Up", for: .normal) button.sizeToFit() button.addTarget(self, action: #selector(self.voteUp), for: .touchUpInside) // (L.3) self.view.addSubview(button) // (L.4) return button // (L.5) }() lazy var voteDownButton: UIButton! = { let button: UIButton = UIButton(type: .system) button.setTitle("Vote Down", for: .normal) button.sizeToFit() button.addTarget(self, action: #selector(self.voteDown), for: .touchUpInside) self.view.addSubview(button) return button }() lazy var chuckWhoButton: UIButton! = { let button: UIButton = UIButton(type: .system) button.setTitle("Chuck Who?", for: .normal) button.sizeToFit() button.addTarget(self, action: #selector(self.chuckWho), for: .touchUpInside) self.view.addSubview(button) return button }() 

El código de inicialización para los tres botones es el mismo.

(L.1) Declaramos una variable perezosa y la inicializamos con un cierre.
(L.2) Dentro del cierre, definimos una variable local para el botón, un tipo de sistema, especificamos el nombre y el tamaño.
(L.3) Asociamos con el botón la acción correspondiente cuando se presiona.
(L.4) Agregar a la jerarquía de vistas.
(L.5) Y asignamos esta variable local a nuestra variable perezosa.

A continuación, en MainViewController.swift, inserte el código del método para los botones

MainViewController.swift

 class MainViewController: UIViewController { … func retrieveRandomJokes() { … } // TODO:    } // end of class MainViewController 

Código de método para botones
 func voteUp() { let http: HTTPCommunication = HTTPCommunication() // (M.1) let params: [String: Int] = ["joke_id": self.jokeID, "vote": 1] // (M.2) if let url = URL(string: "http://example.com/rater/vote") { // (M.3) http.postURL(url, params: params) { (data) in // (M.4) print("\(data). Voted Up") } } } func voteDown() { let http: HTTPCommunication = HTTPCommunication() let params: [String: Int] = ["joke_id": self.jokeID, "vote": -1] if let url = URL(string: "http://example.com/rater/vote") { http.postURL(url, params: params) { (data) in print("\(data). Voted Down") } } } func chuckWho() { } 

El código de los métodos para votar arriba y abajo es el mismo.

(M.1) Cree un objeto de clase para la comunicación con el servidor.
(M.2) Creamos un diccionario de parámetros que incluye la identificación del chiste por el que votamos, y el valor real del voto es +1 o -1.
(M.3) A continuación, obtenemos de manera segura la URL de la barra de direcciones de nuestro dominio de capacitación http://example.com/rater/vote.
(M.4) Y enviamos a través de POST una solicitud de URL de nuestros parámetros. E imprima en la consola una línea que indique la finalización de la solicitud.

A continuación, en MainViewController.swift reescribimos el código del método configStackView, teniendo en cuenta los cambios realizados en la interfaz de usuario.

MainViewController.swift

 func configStackView() -> UIStackView { let innerStackView: UIStackView = UIStackView(arrangedSubviews: [self.voteUpButton, self.voteDownButton]) // (N.1) innerStackView.axis = .horizontal // (N.2) innerStackView.distribution = .fillEqually let mainStackView: UIStackView = UIStackView(arrangedSubviews: [self.jokeLabel, innerStackView, self.chuckWhoButton]) // (N.3) mainStackView.spacing = 50 // (N.4) mainStackView.axis = .vertical mainStackView.distribution = .fillEqually self.view.addSubview(mainStackView) // (N.5) return mainStackView } 

(N.1) Cree un innerStackView que contenga dos botones para votar.
(N.2) Exponemos el eje vertical; La distribución es la misma.
(N.3) En mainStackView principal, agregue innerStackView y el botón sobre Chuck Norris a la etiqueta de la última parte.
(N.4) Configure mainStackView.
(N.5) Agregue mainStackView a la jerarquía de vistas.

Lanzamos nuestra aplicación y vemos la interfaz como en la Fig. 1


Fig. 1 La interfaz de nuestra aplicación con botones adicionales para votar

Verificamos que al votar a favor o en contra de una broma, se muestran los siguientes mensajes en la consola:

345 bytes. Voted Up

o

345 bytes. Voted Down

que indica una solicitud POST completada con éxito al servidor.

Escribimos una aplicación de broma usando icndb API y GET HTTP verb. Pudimos mostrar estos chistes en UIView y todos los chistes se pueden apreciar. Estas acciones envían una solicitud POST al servidor remoto, lo que debería guardar nuestra estimación.

Usar webViews para mostrar páginas web


Agregaremos una vista web para mostrar la página de Wikipedia sobre Chuck Norris. Ella se confunde con solo tocar un botón.

En el archivo WebViewController.swift, escriba el siguiente código auxiliar de función:

WebViewController.swift

 import UIKit class WebViewController: UIViewController { lazy var webView: UIWebView = { // (O.1) self.configWebView() }() lazy var toolbar: UIToolbar = { // (O.2) self.configToolbar() }() override func viewDidLoad() { super.viewDidLoad() } func back() { // (O.3) self.webView.goBack() } func forward() { self.webView.goForward() } } extension WebViewController { func configWebView() -> UIWebView { // (O.4) } func configToolbar() -> UIToolbar { // (O.5) } func configConstraints() { // (O.6) } } 

(O.1) Creamos variables perezosas para webView
(O.2) y barra de herramientas.
(O.3) Creamos métodos de ida y vuelta, para la navegación en el historial de visitas de enlaces.
(O.4) Agregamos métodos de inicialización de webView a la extensión
(O.5) y barra de herramientas
(O.6) y restricciones para la interfaz.

Ahora pintamos el código de cada función:

 func configWebView() -> UIWebView { let webView: UIWebView = UIWebView() // (P.1) self.view.addSubview(webView) return webView } 

(P.1) Inicializamos webView y agregamos la vista a la jerarquía.

 func configToolbar() -> UIToolbar { let toolbar: UIToolbar = UIToolbar(frame: CGRect.zero) // (Q.1) let backButton: UIBarButtonItem = UIBarButtonItem(title: "<", style: .plain, target: self, action: #selector(self.back)) // (Q.2) let forwardButton: UIBarButtonItem = UIBarButtonItem(title: ">", style: .plain, target: self, action: #selector(self.forward)) toolbar.setItems([backButton, forwardButton], animated: false) // (Q.3) toolbar.backgroundColor = UIColor.lightGray // (Q.4) self.view.addSubview(toolbar) return toolbar } 

(P.1) Cree una barra de herramientas con un marco cero.
(P.2) Creamos dos botones para navegar por el historial de enlaces de visita. Con los nombres "<" y ">" y acciones asociadas al método correspondiente en webView.
(Q.3) Agregue estos botones a la barra de herramientas.
(P.4) Establezca el color de fondo, como la barra de navegación, gris claro. Y agregue la vista a la jerarquía.

 func configConstraints() { self.webView.translatesAutoresizingMaskIntoConstraints = false // (R.1) NSLayoutConstraint.activate([ // (R.2) self.webView.topAnchor.constraint(equalTo: self.topLayoutGuide.topAnchor), self.webView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor), self.webView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor), self.webView.bottomAnchor.constraint(equalTo: self.toolbar.topAnchor) ]) self.toolbar.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ // (R.3) self.toolbar.leadingAnchor.constraint(equalTo: self.view.leadingAnchor), self.toolbar.trailingAnchor.constraint(equalTo: self.view.trailingAnchor), self.toolbar.bottomAnchor.constraint(equalTo: self.view.bottomAnchor), self.toolbar.heightAnchor.constraint(equalToConstant: 44.0) ]) } 

(R.1) Deshabilite las máscaras de tamaño automático para que no entren en conflicto con nuestras restricciones
(R.2) Para webView, configure los cuatro lados de la vista.
(R.3) Para la barra de herramientas, configure tres lados de la vista, excepto la parte superior, pero establezca adicionalmente la altura en el tamaño estándar 44.0.

Y reescriba el código ViewDidLoad () de la siguiente manera:

 override func viewDidLoad() { super.viewDidLoad() self.configConstraints() // (S.1) if let url = URL(string: "http://en.wikipedia.org/wiki/Chuck_Norris") { // (S.2) let request: URLRequest = URLRequest(url: url) // (S.3) self.webView.loadRequest(request) // (S.4) } } 

(S.1) Invoque el establecimiento de restricciones.
(S.2) Obtenga de forma segura la URL de la barra de direcciones de la página de Wikipedia en.wikipedia.org/wiki/Chuck_Norris
(S.3) Creamos solicitud de url.
(S.4) Y ejecutamos la solicitud dada en webView.

Regresamos al MainViewController y lo llenamos con el método de llamar a información sobre Chuck Norris.

MainViewController.swift

 func chuckWho() { self.navigationController?.show(WebViewController(), sender: self) // (T.1) } 

(T.1) Cuando haga clic en este botón en la pila de NavigationController, coloque una nueva instancia de webView.

Fig. 2 demuestra cómo se ve webView en nuestra aplicación.


Fig. 2. La vista final de la webView que hemos implementado

Source: https://habr.com/ru/post/es415615/


All Articles