Cet article fait suite à l'article
Interagir avec le serveur via une API dans iOS sur Swift 3. Partie 1 et mettre à jour un
ancien article écrit en Objective-C sur swift 3.
Brève théorie
GET request
GET est la méthode de requête HTTP la plus simple, et c'est ce que le navigateur utilise pour charger les pages Web. Il est utilisé pour demander du contenu situé à une URL spécifique. Le contenu peut être, par exemple, une page Web, une image ou un fichier audio. Par convention, les requêtes GET sont en lecture seule et, conformément à la norme W3C, ne doivent pas être utilisées dans des opérations qui changent le côté serveur. Par exemple, nous n'utiliserons pas de requête GET pour envoyer un formulaire ou envoyer une photo, car ces opérations nécessitent quelques modifications côté serveur (nous utiliserons POST dans ces cas).
Demande POST
POST envoie des données pour un traitement ultérieur à l'URL. Les paramètres sont inclus dans le corps de la demande en utilisant le même format que GET. Par exemple, si nous voulons publier un formulaire contenant deux champs, un nom et un âge, nous enverrons quelque chose de similaire à name = Martin & age = 29 dans le corps de la demande.
Cette méthode d'envoi de paramètres est largement utilisée dans les pages Web. Les cas les plus courants sont les formulaires. Lorsque nous remplissons le formulaire sur le site et cliquons sur Soumettre, la demande sera probablement POST.
Dans notre application, nous utilisons POST pour classer les blagues. Nous enverrons des votes (ou +1 ou -1) au serveur distant.
Les données de demande POST peuvent être structurées en utilisant différents formats. Les paramètres sont généralement formatés conformément aux normes de codage form-url (conformément à la norme HTML W3C). Il s'agit du format par défaut et il est largement utilisé dans de nombreux navigateurs. Notre méthode accepte le dictionnaire Dictionary comme argument, mais nous ne pouvons pas envoyer le dictionnaire Dictionary via la connexion HTTP car il s'agit d'un type Swift interne. Pour transmettre via une connexion HTTP, nous devons créer une représentation reconnaissable du dictionnaire. C'est comme communiquer avec un étranger. Nous traduisons notre message dans une langue universelle, et il traduit d'une langue universelle dans sa langue maternelle. Le langage universel en HTTP est le standard W3C, notre langue est Swift, la langue du destinataire nous est inconnue.
La norme W3C définit des règles qui définissent ce que signifie une représentation reconnaissable pour chaque cas. Dans notre cas, nous devons présenter les paramètres suivant la partie codée par l'url du formulaire de la norme (par exemple, param1 = var1 & param2 = var2).
Webview
À l'aide d'outils de langage de programmation, nous pouvons envoyer des demandes à un serveur distant. C'est ce que font les navigateurs avant d'afficher une page Web. La différence réside uniquement dans le contenu de la réponse. Les pages Web sont formatées à l'aide de la norme HTML, qui définit un ensemble de règles pour définir graphiquement différentes balises de balisage. Ces règles semblent simples, mais afficher une page entière suivant la norme W3C est une tâche difficile. Heureusement, iOS a un composant UIWebView intégré qui utilise le moteur WebKit bien connu et interprète HTML / CSS / JavaScript et affiche des pages Web entières dans une UIView.
Il y a plusieurs cas où nous voulons contrôler le flux de navigation. Par exemple, nous voulons savoir quand un certain contenu ou une URL spécifique est chargé.
Ou peut-être que nous mettons en œuvre un navigateur sécurisé pour les enfants, nous voulons empêcher l'utilisateur de charger des pages qui répondent à certains critères, comme le sexe ou la drogue. Pour tous ces types de personnalisation, nous créons une instance d'une classe qui implémente le protocole UIWebViewDelegate en tant que délégué UIWebView. Nous pouvons implémenter les méthodes suivantes:
webView:shouldStartLoadWithRequest:navigationType: webViewDidStartLoad: webViewDidFinishLoad: webView:didFailLoadWithError:
En utilisant la première méthode, nous pouvons contrôler le flux de navigation en autorisant ou en bloquant des requêtes spécifiques. Les trois autres méthodes sont des événements d'information (les noms de méthode donnent une bonne idée de l'événement).
Mise en place du vote via demande POST
Nous ajouterons les
fichiers MainViewController.swift et
HTTPCommunication.swift et créerons un nouveau fichier
WebViewController.swift dans lequel nous définirons notre webView.
Créez d'abord un nouveau fichier WebViewController.swift et définissez-y la classe WebViewController:
WebViewController.swift import UIKit class WebViewController: UIViewController { } extension WebViewController { }
Maintenant, nous implémentons la fonctionnalité permettant de faire des requêtes POST dans la classe responsable de toutes nos opérations HTTP: classe HTTPCommunication. Pour ce faire, ajoutez une nouvelle méthode postURL (_: params: complétementHandler :) dans HTTPCommunication.swift, qui est similaire à la méthode précédente retrieveURL (_: complétionHandler :).
HTTPCommunication.swift class HTTPCommunication: NSObject { … func retrieveURL(_ url: URL, completionHandler: @escaping ((Data) -> Void)) { … }
Code de méthode
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) Avec la fermeture, nous travaillerons en dehors de cette fonction, nous la désignons donc par @escaping.
(K.2) Nous stockons la fermeture réussie dans la propriété d'achèvementHandler.
(K.3) Nous écrivons une méthode qui convertit le dictionnaire des paramètres passés sous forme de chaîne, puis encodons cette chaîne en type de données dans le codage utf8.
(K.4) Nous créons une requête avec l'URL transmise et la configurons: définissez POST comme méthode, et définissez nos paramètres de type Data avec utf8 comme corps de requête.
(K.5) Enfin, créez une session avec une configuration par défaut et un délégué comme nous-mêmes. Et nous créons une tâche pour charger le résultat de cette demande dans un stockage temporaire. À la fin, décongelez la tâche.
Ajoutons maintenant l'interface nécessaire à MainViewController.swift. Ajoutez deux boutons pour voter voteUp et voteDown, ce qui augmentera ou diminuera respectivement la note de la blague actuelle. Nous ajoutons également le bouton «Chuck Who?», Que nous remplirons de fonctionnalités dans la section sur la vue Web.
Dans le MainViewController.swift, insérez le code d'initialisation voteUpButton, voteDownButton et chuckWhoButton.
MainViewController.swift lazy var jokeLabel: UILabel! = … var jokeID: Int! lazy var activityView: UIActivityIndicatorView! = …
Code d'initialisation du bouton
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 }()
Le code d'initialisation pour les trois boutons est le même.
(L.1) Nous déclarons une variable paresseuse et l'initialisons avec une fermeture.
(L.2) À l'intérieur de la fermeture, nous définissons une variable locale pour le bouton, un type de système, spécifions le nom et la taille.
(L.3) Nous associons au bouton l'action correspondante lorsqu'il est pressé.
(L.4) Ajouter à la hiérarchie des vues.
(L.5) Et nous attribuons cette variable locale à notre variable paresseuse.
Ensuite, dans MainViewController.swift, insérez le code de méthode pour les boutons
MainViewController.swift class MainViewController: UIViewController { … func retrieveRandomJokes() { … }
Code de méthode pour les boutons
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() { }
Le code des méthodes de vote de haut en bas est le même.
(M.1) Créez un objet de classe pour la communication avec le serveur.
(M.2) Nous créons un dictionnaire de paramètres qui inclut l'id de la blague pour laquelle nous votons, et la valeur réelle du vote est +1 ou -1.
(M.3) Ensuite, nous obtenons l'URL en toute sécurité dans la barre d'adresse de notre domaine de formation
http://example.com/rater/vote.
(M.4) Et nous envoyons via POST une demande d'url de nos paramètres. Et imprimez sur la console une ligne indiquant la fin de la demande.
Ensuite, dans MainViewController.swift, nous réécrivons le code de la méthode configStackView, en tenant compte des modifications apportées à l'interface utilisateur.
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) Créez un innerStackView contenant deux boutons pour voter.
(N.2) Nous exposons l'axe vertical; la distribution est la même.
(N.3) Dans le mainStackView principal, ajoutez le innerStackView et le bouton sur Chuck Norris à l'étiquette de la dernière partie.
(N.4) Configurez mainStackView.
(N.5) Ajoutez mainStackView à la hiérarchie des vues.
Nous lançons notre application et voyons l'interface comme sur la Fig. 1
Fig.1 L'interface de notre application avec des boutons ajoutés pour voternous vérifions que lorsque vous votez pour ou contre une blague, les messages suivants s'affichent dans la console:
345 bytes. Voted Up
ou
345 bytes. Voted Down
qui indique une requête POST réussie au serveur.
Nous avons écrit une application de blague en utilisant l'API icndb et le verbe HTTP GET. Nous avons pu montrer ces blagues sur UIView et chaque blague peut être appréciée. Ces actions envoient une requête POST au serveur distant, ce qui devrait enregistrer notre estimation.
Utilisation de webViews pour afficher des pages Web
Nous allons ajouter une vue Web pour afficher la page wikipedia de Chuck Norris. Elle devient confuse au simple toucher d'un bouton.
Dans le fichier WebViewController.swift, écrivez le stub de fonction suivant:
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) Nous créons des variables paresseuses pour webView
(O.2) et barre d'outils.
(O.3) Nous créons des méthodes d'avant en arrière, pour la navigation sur l'historique de visite des liens.
(O.4) Nous ajoutons des méthodes d'initialisation webView à l'extension
(O.5) et toolBar
(O.6) et contraintes pour l'interface.
Maintenant, nous peignons le code de chaque fonction:
func configWebView() -> UIWebView { let webView: UIWebView = UIWebView()
(P.1) Nous initialisons la webView et ajoutons la vue à la hiérarchie.
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 }
(Q.1) Créez une barre d'outils avec un cadre nul.
(Q.2) Nous créons deux boutons pour naviguer dans l'historique des liens de visite. Avec les noms «<» et «>» et les actions attachés à la méthode correspondante dans webView.
(Q.3) Ajoutez ces boutons à la barre d'outils.
(Q.4) Définissez la couleur d'arrière-plan, comme la barre de navigation, gris clair. Et ajoutez la vue à la hiérarchie.
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) Désactiver les masques de redimensionnement automatique afin qu'ils n'entrent pas en conflit avec nos contraintes
(R.2) Pour webView, configurez les quatre côtés de la vue.
(R.3) Pour la barre d'outils, configurez trois côtés de la vue, à l'exception du haut, mais définissez également la hauteur à la taille standard 44,0.
Et réécrivez le code ViewDidLoad () comme suit:
override func viewDidLoad() { super.viewDidLoad() self.configConstraints()
(S.1) Invoquer la définition de contraintes.
(S.2) Obtenez l'URL en toute sécurité dans la barre d'adresse de la page Wikipedia
en.wikipedia.org/wiki/Chuck_Norris(S.3) Nous créons une demande à partir de l'URL.
(S.4) Et nous exécutons la demande donnée dans webView.
Nous revenons au MainViewController et le remplissons avec la méthode d'appel des informations sur Chuck Norris.
MainViewController.swift func chuckWho() { self.navigationController?.show(WebViewController(), sender: self)
(T.1) Lorsque vous cliquez sur ce bouton dans la pile navigationController, placez une nouvelle instance de webView.
Fig. 2 montre à quoi ressemble webView dans notre application.
Fig. 2. La vue finale de la webView que nous avons implémentée