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:
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)
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.

Akhirnya, lelucon dimuat dan ditampilkan.

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.