Interaksi dengan server melalui API di iOS pada Swift 3. Bagian 1

Artikel ini adalah pembaruan untuk artikel Memperoleh data jarak jauh di iOS , ditulis pada November 2015 menggunakan Objective-C dan karenanya usang secara moral. Sekarang, kode akan diberikan yang telah ditulis ulang ke Swift 3 dan iOS 10 (versi terbaru adalah Swift 4.1 dan iOS 11, tetapi komputer saya tidak lagi mendukungnya).

Teori singkat


Format url


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

  • http adalah protokol yang menentukan dengan standar apa permintaan dibuat. Opsi lainnya: https, ftp, file
  • www.google.com - nama domain
  • / - direktori tempat sumber daya yang kami butuhkan berada.
  • Setelah tanda tanya (?) Buka parameter q = Hello & safe = off. Mereka terdiri dari pasangan kunci-nilai.
  • Permintaan juga menunjukkan metode yang memberi tahu bagaimana server harus menangani permintaan ini. Secara default, ini adalah metode GET.

Url dari contoh ini dapat dibaca sebagai berikut: permintaan http dengan metode GET dikirim ke google.com, di direktori root /, dengan dua parameter q dengan Hello dan safe with off.

Header http


Peramban mengonversi string url ke tajuk dan badan permintaan. Untuk permintaan http, isi kosong, dan tajuknya adalah sebagai berikut

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

Diagram permintaan server


Pertama, permintaan dibuat, lalu koneksi dibuat, permintaan dikirim dan respons diterima.

Delegasi sesi


Semua operasi UI (terkait dengan antarmuka pengguna) dilakukan di utas utama. Anda tidak bisa hanya mengambil dan menghentikan utas ini saat beberapa jenis operasi intensif sumber daya sedang dilakukan. Oleh karena itu, salah satu solusi untuk masalah ini adalah membuat delegasi. Dengan demikian, operasi menjadi tidak sinkron, dan utas utama berjalan tanpa henti. Ketika operasi yang diperlukan selesai, metode delegasi yang sesuai akan dipanggil. Solusi kedua adalah membuat utas eksekusi baru.

Seperti dalam buku aslinya, kami menggunakan delegasi sehingga operasi lebih jelas dibagi antara metode. Meskipun kode lebih kompak melalui blok.

Deskripsi jenis delegasi sesi


Kami menggunakan NSURLSessionDownloadDelegate dan menerapkan metode URLSession: unduhTask: didFinishDownloadingToURL :. Faktanya, kami mengunduh data dengan lelucon ke penyimpanan sementara, dan ketika unduhan selesai, kami memanggil metode delegasi untuk diproses.

Transisi ke aliran utama


Pemuatan data ke penyimpanan sementara tidak dilakukan di arus utama, tetapi untuk menggunakan data ini untuk mengubah UI, kami akan pergi ke arus utama.

Runaway Closure (@escaping)


Karena, karena implementasi kode, penutupan yang kami sampaikan ke metode pemuatan data dari url akan bertahan dari metode itu sendiri, untuk Swift 3 perlu untuk secara eksplisit menunjuknya pada @ pelarian, dan menjadikan diri tidak dimiliki sehingga tautan mandiri tidak menangkap dan menahan dalam penutupan ini. Tapi ini adalah nuansa implementasi bahasa Swift itu sendiri, dan bukan teknologi penerimaan data oleh API.

Arahan ulang (arahan ulang)


Dalam beberapa kasus, pengalihan terjadi. Sebagai contoh, jika kita memiliki beberapa url pendek, maka ketika kita memasukkannya ke dalam bilah pencarian browser, browser pertama kali pergi ke server, di mana url pendek ini didekripsi dan dikirim kepada kami, dan kemudian kita pergi ke server target menggunakan url lengkap ini. Jika perlu, kita dapat mengontrol pengalihan ini menggunakan NSURLSessionTaskDelegate , tetapi secara default, NSURLSession menangani semua detail.

Skema serialisasi


Serialisasi adalah proses mentransfer data dari satu jenis penyimpanan ke yang lain, tanpa kehilangan konten. Sebagai contoh, data disimpan dalam bentuk biner untuk mengambil lebih sedikit ruang, dan ketika dikirim melalui jaringan, data tersebut dikonversi ke format universal JSON (JavaScript Object Notation), yang telah kami dekripsi dan terjemahkan menjadi objek-objek di lingkungan pemrograman kami.

Contoh JSON:

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

Kurung keriting menunjukkan kamus, dan objek di dalam kamus diwakili oleh pasangan nilai kunci.

API (Antarmuka Pemrograman Aplikasi)


Dalam kasus kami, API diwakili oleh alamat dari mana kami akan menerima lelucon acak dan format respons JSON, yang perlu kami uraikan ke dalam struktur yang nyaman untuk manipulasi

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

Contoh icndb API:

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

Sekarang berlatih


Seluruh proyek, seperti yang terakhir kali, diimplementasikan dalam kode, tanpa menggunakan storyboard. Semua kode ditulis dalam 3 file: AppDelegate.swift , MainViewController.swift dan HTTPCommunication.swift . AppDelegate.swift berisi konfigurasi umum aplikasi. HTTPCommunication.swift mengkonfigurasi koneksi (permintaan, sesi) dan menerima data. Di MainViewController.swift, data ini diserialisasi untuk keluaran, dan juga berisi kode antarmuka pengguna.

Buat proyek kosong. Untuk mempermudah, kami menulis aplikasi hanya untuk iPhone . Kami menghapus ViewController.swift , Main.storyboard dan di Info.plist kami juga menghapus tautan ke storyboard, yaitu baris nama file file storyboard Utama - String - Main .

Secara default, App Transport Security di iOS memblokir unduhan dari Internet menggunakan http biasa (bukan https), jadi kami membuat perubahan ke Info.plist , seperti yang ditunjukkan di bawah ini. Untuk melakukan ini, buka Info.plist sebagai kode sumber , lalu tambahkan kode berikut:

 <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> 

Secara default, kami melarang unduhan sewenang-wenang: kunci NSAllowsArbitraryLoads salah. Tapi kami menambahkan sebagai pengecualian, domain kami dengan lelucon dan semua subdomain: nilai kunci NSExceptionDomains.

Sekarang di AppDelegate.swift kami menulis ulang aplikasi (_: didFinishLaunchingWithOptions :) sebagai berikut:

 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 } 


Buat file HTTPCommunication.swift . Dan kami menulis kode berikut di dalamnya.

 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) { } } 


Sekarang kita akan menulis kode fungsi-fungsi ini.

Salin kode ambilURL (_ 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() } 


Salin kode fungsi urlSession (_ sesi:, 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.") } } 


Kami membuat file MainViewController.swift dan menyalin kode berikut, yang menciptakan antarmuka yang diperlukan:

 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) ]) } } 


Kami telah menemukan antarmuka, sekarang Anda dapat mengisi fungsionalitasnya.

Berikut ini kode recoverveRandomJokes ()

 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.") } } } 


Sekarang jalankan aplikasi dan dapatkan hasil berikut.

Sementara kami sedang menunggu lelucon dari situs.

gambar

Akhirnya, lelucon dimuat dan ditampilkan.

gambar

Pada artikel selanjutnya, kita akan melihat bagian kedua dari aplikasi yang ditulis ulang dengan cepat, yang memungkinkan Anda untuk menerima lelucon baru tanpa memulai kembali program, serta memilih lelucon.

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


All Articles