Interaktion mit dem Server über die API in iOS unter Swift 3. Teil 1

Dieser Artikel ist eine Aktualisierung des Artikels Abrufen von Remote-Daten in iOS , der im November 2015 mit Objective-C geschrieben wurde und daher moralisch veraltet ist. Jetzt wird der Code angezeigt, der in Swift 3 und iOS 10 umgeschrieben wurde (die neueste Version ist Swift 4.1 und iOS 11, aber mein Computer unterstützt sie nicht mehr).

Kurze Theorie


URL-Format


http://www.google.com/?q=Hello&safe=off 

  • http ist ein Protokoll, das bestimmt, nach welchem ​​Standard eine Anfrage gestellt wird. Weitere Optionen: https, ftp, Datei
  • www.google.com - Domainname
  • / - Verzeichnis, in dem sich die benötigten Ressourcen befinden.
  • Nach dem Fragezeichen (?) Gehen Sie zu den Parametern q = Hello & safe = off. Sie bestehen aus Schlüssel-Wert-Paaren.
  • Die Anforderung gibt auch eine Methode an, die angibt, wie der Server diese Anforderung verarbeiten soll. Standardmäßig ist dies eine GET-Methode.

Diese URL aus dem Beispiel kann wie folgt gelesen werden: Eine http-Anfrage mit der GET-Methode wird an google.com im Stammverzeichnis / mit zwei q-Parametern mit Hello und safe mit off gesendet.

http-Header


Der Browser konvertiert die URL-Zeichenfolge in den Header und den Text der Anforderung. Bei einer http-Anforderung ist der Text leer und der Header lautet wie folgt

 GET /?q=Hello&safe=off HTTP/1.1 Host: google.com Content-Length: 133 //    //     

Serveranforderungsdiagramm


Zuerst wird eine Anfrage erstellt, dann wird eine Verbindung hergestellt, eine Anfrage gesendet und eine Antwort empfangen.

Sitzungsdelegierte


Alle UI-Operationen (die der Benutzeroberfläche zugeordnet sind) werden im Hauptthread ausgeführt. Sie können diesen Thread nicht einfach nehmen und stoppen, während eine ressourcenintensive Operation ausgeführt wird. Daher bestand eine der Lösungen für dieses Problem darin, Delegierte zu erstellen. Somit werden Operationen asynchron und der Hauptthread wird ohne Unterbrechung ausgeführt. Wenn der erforderliche Vorgang abgeschlossen ist, wird die entsprechende Delegatmethode aufgerufen. Die zweite Lösung besteht darin, einen neuen Ausführungsthread zu erstellen.

Wie im ursprünglichen Buch verwenden wir einen Delegaten, damit die Operationen klarer zwischen den Methoden aufgeteilt werden. Obwohl der Code durch Blöcke kompakter ist.

Beschreibung der Arten von Sitzungsdelegierten


Wir verwenden NSURLSessionDownloadDelegate und implementieren dessen Methode URLSession: downloadTask: didFinishDownloadingToURL:. Das heißt, wir laden die Daten mit einem Witz in einen temporären Speicher herunter, und wenn der Download abgeschlossen ist, rufen wir die Delegate-Methode zur Verarbeitung auf.

Übergang zum Hauptstrom


Das Laden von Daten in den temporären Speicher wird nicht im Hauptstrom ausgeführt. Um diese Daten jedoch zum Ändern der Benutzeroberfläche zu verwenden, werden wir zum Hauptstrom wechseln.

Runaway Closure (@escaping)


Da aufgrund der Implementierung des Codes der Abschluss, den wir an die Methode zum Laden von Daten aus der URL übergeben, die Methode selbst überlebt, muss Swift 3 ihn explizit als @escaping und self als nicht besessen kennzeichnen, damit der Self-Link diesen Abschluss nicht erfasst und festhält. Dies sind jedoch die Nuancen der Implementierung der Swift-Sprache selbst und nicht die Technologie zum Empfangen von Daten per API.

Weiterleitungen (Weiterleitungen)


In einigen Fällen treten Weiterleitungen auf. Wenn wir beispielsweise eine kurze URL haben, geht der Browser beim Eingeben in die Suchleiste des Browsers zuerst zum Server, wo diese kurze URL entschlüsselt und an uns gesendet wird, und dann mit dieser vollständigen URL zum Zielserver. Bei Bedarf können wir diese Weiterleitungen mit NSURLSessionTaskDelegate steuern. Standardmäßig verarbeitet NSURLSession jedoch alle Details.

Serialisierungsschema


Bei der Serialisierung werden Daten ohne Verlust von Inhalten von einem Speichertyp auf einen anderen übertragen. Beispielsweise werden Daten in binärer Form gespeichert, um weniger Speicherplatz zu beanspruchen. Wenn sie über ein Netzwerk gesendet werden, werden sie in ein universelles JSON-Format (JavaScript Object Notation) konvertiert, das wir bereits in unserer Programmierumgebung entschlüsseln und in Objekte übersetzen.

JSON-Beispiel:

 { "name": "Martin Conte Mac Donell", "age": 29, "username": "fz" } 

Geschweifte Klammern kennzeichnen ein Wörterbuch, und Objekte in einem Wörterbuch werden durch Schlüssel-Wert-Paare dargestellt.

API (Application Programming Interface)


In unserem Fall wird die API durch die Adresse dargestellt, von der wir zufällige Witze und JSON-Antwortformate erhalten, die wir in Strukturen analysieren müssen, die für die Manipulation geeignet sind

 http://api.icndb.com/jokes/random 

Beispiel icndb API:

 { "type": "success", "value": { "id": 201, "joke": "Chuck Norris was what Willis was talkin' about" } } 

Jetzt übe


Das gesamte Projekt wird wie beim letzten Mal ohne Verwendung eines Storyboards in Code implementiert. Der gesamte Code wird in 3 Dateien geschrieben: AppDelegate.swift , MainViewController.swift und HTTPCommunication.swift . AppDelegate.swift enthält die allgemeine Konfiguration der Anwendung. HTTPCommunication.swift konfiguriert die Verbindung (Anfrage, Sitzung) und empfängt Daten. In MainViewController.swift werden diese Daten für die Ausgabe serialisiert und enthalten auch Benutzeroberflächencode.

Erstellen Sie ein leeres Projekt. Der Einfachheit halber schreiben wir eine Anwendung nur für das iPhone . Wir löschen ViewController.swift , Main.storyboard und in Info.plist löschen wir auch den Link zum Storyboard, nämlich die Zeile Main Storyboard File Base Name - String - Main .

Standardmäßig blockiert App Transport Security unter iOS das Herunterladen aus dem Internet über reguläres http (nicht https). Daher nehmen wir Änderungen an Info.plist vor , wie unten gezeigt. Öffnen Sie dazu Info.plist als Quellcode und fügen Sie den folgenden Code hinzu:

 <key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <false/> <key>NSExceptionDomains</key> <dict> <key>api.icndb.com</key> <dict> <key>NSExceptionAllowsInsecureHTTPLoads</key> <true/> <key>NSIncludesSubdomains</key> <true/> </dict> </dict> </dict> 

Standardmäßig verbieten wir willkürliche Downloads: Der Schlüssel NSAllowsArbitraryLoads ist falsch. Aber wir fügen ausnahmsweise unsere Domain mit Witzen und allen Subdomains hinzu: Schlüsselwerte NSExceptionDomains.

Jetzt schreiben wir in AppDelegate.swift die Anwendung (_: didFinishLaunchingWithOptions :) wie folgt um:

 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { self.window = UIWindow(frame: UIScreen.main.bounds) //  MainViewController   NavigationController, //     . let navC: UINavigationController = UINavigationController(rootViewController: MainViewController()) self.window?.rootViewController = navC self.window?.backgroundColor = UIColor.white self.window?.makeKeyAndVisible() return true } 


Erstellen Sie die Datei HTTPCommunication.swift . Und wir schreiben den folgenden Code hinein.

 import UIKit //   NSObject,   (conform) NSObjectProtocol, //   URLSessionDownloadDelegate    , //     ,     . class HTTPCommunication: NSObject { //  completionHandler   -  ,   //           //    . var completionHandler: ((Data) -> Void)! // retrieveURL(_: completionHandler:)    //  url    func retrieveURL(_ url: URL, completionHandler: @escaping ((Data) -> Void)) { } } //    ,    NSObject //  (conforms)  URLSessionDownloadDelegate, //        //  . extension HTTPCommunication: URLSessionDownloadDelegate { //        //         . func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { } } 


Jetzt werden wir den Code dieser Funktionen schreiben.

Kopieren Sie den Code retrieveURL (_ url :, CompletionHandler :)

 //        , //     @escaping. func retrieveURL(_ url: URL, completionHandler: @escaping ((Data) -> Void)) { self.completionHandler = completionHandler let request: URLRequest = URLRequest(url: url) let session: URLSession = URLSession(configuration: .default, delegate: self, delegateQueue: nil) let task: URLSessionDownloadTask = session.downloadTask(with: request) //        , //   . task.resume() } 


Kopieren Sie den Code func urlSession (_ session :, downloadTask :, didFinishDownloadingTo :)

 func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { do { //         //  .      // ,   try,     //   do {} catch {} let data: Data = try Data(contentsOf: location) //    completionHandler   . //         , //     ,    //  ,       . DispatchQueue.main.async(execute: { self.completionHandler(data) }) } catch { print("Can't get data from location.") } } 


Wir erstellen die Datei MainViewController.swift und kopieren den folgenden Code, der die erforderliche Schnittstelle erstellt:

 import UIKit class MainViewController: UIViewController { lazy var jokeLabel: UILabel = { let label: UILabel = UILabel(frame: CGRect.zero) label.lineBreakMode = .byWordWrapping label.textAlignment = .center label.numberOfLines = 0 label.font = UIFont.systemFont(ofSize: 16) label.sizeToFit() self.view.addSubview(label) return label }() //       . var jokeID: Int = 0 // ActivityView   ,    //  ,   . lazy var activityView: UIActivityIndicatorView = { let activityView: UIActivityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: .gray) activityView.hidesWhenStopped = true activityView.startAnimating() view.addSubview(activityView) return activityView }() lazy var stackView: UIStackView = { let mainStackView: UIStackView = UIStackView(arrangedSubviews: [self.jokeLabel]) //        mainStackView.spacing = 50 mainStackView.axis = .vertical mainStackView.distribution = .fillEqually self.view.addSubview(mainStackView) return mainStackView }() override func viewDidLoad() { super.viewDidLoad() self.title = "Chuck Norris Jokes" //     stackView  activityView, //      . //     stackView  //    label. self.configConstraints() // (E.2) //        //     . self.retrieveRandomJokes() // (E.3) } func retrieveRandomJokes() { } } extension MainViewController { func configConstraints() { //   autoresizingMask  (constraints) //  false,        //  self.stackView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ self.stackView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor), self.stackView.leadingAnchor.constraint(equalTo: self.view.layoutMarginsGuide.leadingAnchor), self.stackView.trailingAnchor.constraint(equalTo: self.view.layoutMarginsGuide.trailingAnchor) ]) self.activityView.translatesAutoresizingMaskIntoConstraints = false //    (constraints)  activityView, //      label:   X  Y  //  label  X  Y. NSLayoutConstraint.activate([ self.activityView.centerXAnchor.constraint(equalTo: self.jokeLabel.centerXAnchor), self.activityView.centerYAnchor.constraint(equalTo: self.jokeLabel.centerYAnchor) ]) } } 


Wir haben die Benutzeroberfläche herausgefunden, jetzt können Sie die Funktionalität ausfüllen.

Hier ist der Code retrieveRandomJokes ()

 func retrieveRandomJokes() { let http: HTTPCommunication = HTTPCommunication() //     url  ,    force unwrap  //  url ,      let url: URL = URL(string: "http://api.icndb.com/jokes/random")! http.retrieveURL(url) { //    self  ,  weak self [weak self] (data) -> Void in //      json // ,  ,     .  //     json   , //     . guard let json = String(data: data, encoding: String.Encoding.utf8) else { return } //  : JSON: { "type": "success", "value": // { "id": 391, "joke": "TNT was originally developed by Chuck // Norris to cure indigestion.", "categories": [] } } print("JSON: ", json) do { let jsonObjectAny: Any = try JSONSerialization.jsonObject(with: data, options: []) // ,       Any //    ,    . guard let jsonObject = jsonObjectAny as? [String: Any], let value = jsonObject["value"] as? [String: Any], let id = value["id"] as? Int, let joke = value["joke"] as? String else { return } //     , //       . self.activityView.stopAnimating() self.jokeID = id self.jokeLabel.text = joke } catch { print("Can't serialize data.") } } } 


Führen Sie nun die Anwendung aus und erhalten Sie das folgende Ergebnis.

Während wir auf einen Witz von der Seite warten.

Bild

Schließlich wird der Witz geladen und angezeigt.

Bild

Im nächsten Artikel werden wir uns den zweiten Teil der Anwendung ansehen, der schnell umgeschrieben wurde. So können Sie neue Witze erhalten, ohne das Programm neu zu starten, und für Witze stimmen.

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


All Articles