
Sekarang hampir 100% aplikasi menggunakan jaringan, sehingga semua orang dihadapkan pada organisasi dan penggunaan lapisan jaringan. Ada dua pendekatan utama untuk memecahkan masalah ini, itu baik menggunakan perpustakaan pihak ketiga, atau implementasi Anda sendiri dari lapisan jaringan. Pada artikel ini kita akan mempertimbangkan opsi kedua, dan mencoba menerapkan lapisan jaringan menggunakan semua fitur bahasa terbaru, menggunakan protokol dan enumerasi. Ini akan menyelamatkan proyek dari dependensi yang tidak perlu dalam bentuk perpustakaan tambahan. Mereka yang pernah melihat Moya akan segera mengenali banyak detail serupa dalam implementasi dan penggunaan, seperti itu, hanya saja kali ini kita akan melakukannya sendiri tanpa menyentuh Moya dan Alamofire.
Dalam panduan ini, kita akan melihat bagaimana mengimplementasikan lapisan jaringan pada Swift murni, tanpa menggunakan pustaka pihak ketiga. Setelah Anda meninjau artikel ini, kode Anda akan menjadi
- berorientasi protokol
- mudah digunakan
- mudah digunakan
- ketik aman
- untuk titik akhir enum akan digunakan
Di bawah ini adalah contoh bagaimana tampilan lapisan jaringan kami setelah implementasi:

Dengan hanya menulis 
router.request (. Dan menggunakan semua kekuatan enumerasi, kita akan melihat semua opsi kueri yang mungkin dan parameternya.
Pertama, sedikit tentang struktur proyekSetiap kali Anda membuat sesuatu yang baru, dan agar dapat dengan mudah memahami segala sesuatu di masa depan, sangat penting untuk mengatur dan menyusun semuanya dengan benar. Saya memegang keyakinan bahwa struktur folder yang tertata dengan baik adalah detail penting ketika membangun arsitektur aplikasi. Agar kita dapat mengatur semuanya dengan benar di folder, mari kita buat terlebih dahulu. Ini akan terlihat seperti struktur folder umum dalam proyek:
 Protokol jenis akhir
Protokol jenis akhirPertama-tama, kita perlu mendefinisikan protokol 
EndPointType kita. Protokol ini akan berisi semua informasi yang diperlukan untuk mengonfigurasi permintaan. Apa itu permintaan (titik akhir)? Intinya, ini adalah URLRequest dengan semua komponen terkait, seperti tajuk, parameter permintaan, badan permintaan. Protokol 
EndPointType adalah bagian terpenting dari implementasi lapisan jaringan kami. Mari kita buat file dan beri nama 
EndPointType . Letakkan file ini di folder Layanan (bukan di folder EndPoint, mengapa - ini akan dihapus sedikit kemudian)
 Protokol HTTPEndPointType
Protokol HTTPEndPointType kami berisi beberapa protokol yang perlu kami buat permintaan. Mari kita lihat apa protokol-protokol ini.
HTTPMetodeBuat file, 
beri nama 
HTTPMetode dan letakkan di folder Layanan. Daftar ini akan digunakan untuk mengatur metode HTTP permintaan kami.
 HTTPTask
HTTPTaskBuat file, 
beri nama 
HTTPTask dan letakkan di folder Layanan. HTTPTask bertanggung jawab untuk mengonfigurasi parameter permintaan tertentu. Anda dapat menambahkan sebanyak mungkin opsi kueri yang berbeda sesuai kebutuhan, tetapi saya, pada gilirannya, akan membuat kueri reguler, kueri dengan parameter, kueri dengan parameter dan header, jadi saya hanya akan melakukan tiga jenis kueri ini.

Di bagian selanjutnya, kita akan membahas 
Parameter dan bagaimana kita akan bekerja dengannya
HTTPHeadersHTTPHeaders hanyalah typealias untuk kamus. Anda dapat membuatnya di bagian atas file 
HTTPTask Anda.
public typealias HTTPHeaders = [String:String] 
Parameter & PengkodeanBuat file, beri nama 
ParameterEncoding dan letakkan di folder Encoding. Buat typealias untuk 
Parameter , itu lagi akan menjadi kamus reguler. Kami melakukan ini untuk membuat kode terlihat lebih mudah dimengerti dan dibaca.
 public typealias Parameters = [String:Any] 
Selanjutnya, tentukan protokol 
ParameterEncoder dengan fungsi enkode tunggal. Metode penyandian memiliki dua parameter: 
inout URLRequest dan 
Parameters . 
INOUT adalah kata kunci Swift yang mendefinisikan parameter fungsi sebagai referensi. Biasanya, parameter diteruskan ke fungsi sebagai nilai. Saat Anda menulis 
masukan sebelum parameter fungsi dalam panggilan, Anda menetapkan parameter ini sebagai jenis referensi. Untuk mempelajari lebih lanjut tentang argumen inout, Anda dapat mengikuti tautan ini. Singkatnya, 
inout memungkinkan Anda untuk mengubah nilai variabel itu sendiri, yang diteruskan ke fungsi, dan tidak hanya mendapatkan nilainya dalam parameter dan bekerja dengannya di dalam fungsi. Protokol 
ParameterEncoder akan diimplementasikan dalam 
JSONParameterEncoder dan di 
URLPameterEncoder .
 public protocol ParameterEncoder { static func encode(urlRequest: inout URLRequest, with parameters: Parameters) throws } 
ParameterEncoder berisi fungsi tunggal yang tugasnya menyandikan parameter. Metode ini dapat melempar kesalahan yang perlu ditangani, jadi kami menggunakan throw.
Mungkin juga bermanfaat untuk menghasilkan bukan kesalahan standar, tetapi kesalahan khusus. Selalu cukup sulit untuk mendekripsi apa yang diberikan Xcode kepada Anda. Ketika Anda memiliki semua kesalahan yang disesuaikan dan dijelaskan, Anda selalu tahu persis apa yang terjadi. Untuk melakukan ini, mari kita tentukan enumerasi yang diwarisi dari 
Kesalahan .

Buat file, 
beri nama 
URLParameterEncoder dan letakkan di folder 
Encoding .

Kode ini mengambil daftar parameter, mengonversi, dan memformatnya untuk digunakan sebagai parameter URL. Seperti yang Anda ketahui, beberapa karakter tidak diizinkan di URL. Parameter juga dipisahkan oleh simbol "&", jadi kita harus mengurus ini. Kami juga harus menetapkan nilai default untuk header jika mereka tidak diatur dalam permintaan.
Ini adalah bagian dari kode yang seharusnya dicakup oleh unit test. Membangun permintaan URL adalah kuncinya, jika tidak kita bisa memancing banyak kesalahan yang tidak perlu. Jika Anda menggunakan API terbuka, Anda jelas tidak ingin menggunakan volume permintaan penuh yang mungkin untuk pengujian yang gagal. Jika Anda ingin tahu lebih banyak tentang tes Unit, Anda bisa mulai dengan artikel ini.
JSONParameterEncoderBuat file, 
beri nama 
JSONParameterEncoder dan letakkan di folder Encoding.

Semuanya sama seperti dalam kasus 
URLParameter , hanya di sini kita akan mengkonversi parameter untuk JSON dan kembali menambahkan parameter mendefinisikan encoding "application / json" ke header.
NetworkrouterBuat file, beri nama 
NetworkRouter dan letakkan di folder Layanan. Mari kita mulai dengan menentukan typealias untuk penutupan.
 public typealias NetworkRouterCompletion = (_ data: Data?,_ response: URLResponse?,_ error: Error?)->() 
Selanjutnya, kita mendefinisikan protokol 
NetworkRouter .
 NetworkRouter
NetworkRouter memiliki 
EndPoint yang digunakan untuk permintaan, dan segera setelah permintaan selesai, hasil dari permintaan ini diteruskan ke penutupan 
NetworkRouterCompletion . Protokol juga memiliki fungsi 
batal , yang dapat digunakan untuk mengganggu permintaan bongkar-muat jangka panjang. Kami juga menggunakan jenis 
terkait di sini karena kami ingin 
Router kami mendukung semua jenis 
EndPointType . Tanpa menggunakan tipe terkait, router harus memiliki beberapa tipe spesifik yang mengimplementasikan 
EndPointType . Jika Anda ingin tahu lebih banyak tentang tipe terkait, Anda dapat membaca 
artikel ini .
RouterBuat file, beri nama 
Router dan taruh di folder Layanan. Kami mendeklarasikan variabel pribadi jenis 
URLSessionTask . Semua pekerjaan akan ada di sana. Kami menjadikannya pribadi karena kami tidak ingin siapa pun di luar dapat mengubahnya.
 Minta
MintaDi sini kita membuat 
URLSession menggunakan 
URLSession.share , ini adalah cara termudah untuk membuat. Tetapi ingat bahwa metode ini bukan satu-satunya. Anda dapat menggunakan konfigurasi 
URLSession yang lebih kompleks yang dapat mengubah perilakunya. Lebih lanjut tentang ini di 
artikel ini .
Permintaan dibuat dengan memanggil fungsi 
buildRequest. Panggilan fungsi dibungkus dengan do-try-catch, karena fungsi enkode di dalam 
buildRequest dapat melempar pengecualian. 
Respons , 
data, dan 
kesalahan diteruskan ke penyelesaian.
 Bangun permintaan
Bangun permintaanKami membuat permintaan kami menggunakan fungsi 
buildRequest . Fungsi ini bertanggung jawab untuk semua pekerjaan penting di lapisan jaringan kami. Pada dasarnya mengonversi 
EndPointType ke 
URLRequest . Dan begitu 
EndPoint berubah menjadi permintaan, kita bisa meneruskannya ke 
sesi . Banyak hal yang terjadi di sini, jadi mari kita lihat metodenya. Pertama, 
mari kita periksa metode 
buildRequest :
1. Kami menginisialisasi variabel permintaan 
URLRequest . Kami menetapkan URL basis kami di dalamnya dan menambahkan jalur permintaan khusus yang akan digunakan untuk itu.
2. Tetapkan 
request.httpMetode metode http dari 
EndPoint kami.
3. Kami membuat blok coba-coba-tangkap, karena pembuat enkode kami mungkin melakukan kesalahan. Dengan membuat satu blok do-try-catch besar, kami menghilangkan kebutuhan untuk membuat blok terpisah untuk setiap percobaan.
4. Di sakelar, periksa 
route.task .
5. Bergantung pada jenis tugas, kami memanggil encoder yang sesuai.
 Konfigurasikan Parameter
Konfigurasikan ParameterBuat fungsi 
configureParameters di Router.

Fungsi ini bertanggung jawab untuk mengonversi parameter kueri kami. Karena API kami mengasumsikan penggunaan 
bodyParameters dalam bentuk JSON dan 
URLParameters yang dikonversi ke format URL, kami hanya meneruskan parameter yang sesuai ke fungsi konversi yang sesuai, yang kami jelaskan di awal artikel. Jika Anda menggunakan API yang menyertakan berbagai jenis penyandian, maka dalam hal ini saya akan merekomendasikan menambahkan 
HTTPTask dengan enumerasi tambahan dengan jenis penyandian. Daftar ini harus berisi semua jenis penyandian yang memungkinkan. Setelah itu, di configureParameters tambahkan satu argumen lagi dengan enumerasi ini. Bergantung pada nilainya, beralih menggunakan sakelar dan buat enkode yang Anda butuhkan.
Tambahkan Header TambahanBuat fungsi 
addAdditionalHeaders di Router.

Tambahkan saja semua header yang diperlukan ke permintaan.
BatalkanFungsi 
batal akan terlihat sangat sederhana:
 Contoh penggunaan
Contoh penggunaanSekarang mari kita coba menggunakan layer jaringan kita pada contoh nyata. Kami akan terhubung ke 
TheMovieDB untuk menerima data untuk aplikasi kami.
MovieEndPointBuat file 
MovieEndPoint dan letakkan di folder EndPoint. MovieEndPoint sama dengan
dan TargetType di Moya. Di sini kita menerapkan EndPointType kita sendiri sebagai gantinya. Artikel yang menjelaskan cara menggunakan Moya untuk contoh serupa dapat ditemukan di 
tautan ini .
 import Foundation enum NetworkEnvironment { case qa case production case staging } public enum MovieApi { case recommended(id:Int) case popular(page:Int) case newMovies(page:Int) case video(id:Int) } extension MovieApi: EndPointType { var environmentBaseURL : String { switch NetworkManager.environment { case .production: return "https: 
MoviemodelUntuk mengurai model data 
MovieModel dan JSON ke dalam model, protokol Decodable digunakan. Tempatkan file ini di folder 
Model .
Catatan : untuk perkenalan yang lebih rinci dengan protokol Codable, Decodable, dan Encodable, Anda dapat membaca 
artikel saya yang lain , yang menjelaskan secara rinci semua fitur untuk bekerja dengannya.
 import Foundation struct MovieApiResponse { let page: Int let numberOfResults: Int let numberOfPages: Int let movies: [Movie] } extension MovieApiResponse: Decodable { private enum MovieApiResponseCodingKeys: String, CodingKey { case page case numberOfResults = "total_results" case numberOfPages = "total_pages" case movies = "results" } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: MovieApiResponseCodingKeys.self) page = try container.decode(Int.self, forKey: .page) numberOfResults = try container.decode(Int.self, forKey: .numberOfResults) numberOfPages = try container.decode(Int.self, forKey: .numberOfPages) movies = try container.decode([Movie].self, forKey: .movies) } } struct Movie { let id: Int let posterPath: String let backdrop: String let title: String let releaseDate: String let rating: Double let overview: String } extension Movie: Decodable { enum MovieCodingKeys: String, CodingKey { case id case posterPath = "poster_path" case backdrop = "backdrop_path" case title case releaseDate = "release_date" case rating = "vote_average" case overview } init(from decoder: Decoder) throws { let movieContainer = try decoder.container(keyedBy: MovieCodingKeys.self) id = try movieContainer.decode(Int.self, forKey: .id) posterPath = try movieContainer.decode(String.self, forKey: .posterPath) backdrop = try movieContainer.decode(String.self, forKey: .backdrop) title = try movieContainer.decode(String.self, forKey: .title) releaseDate = try movieContainer.decode(String.self, forKey: .releaseDate) rating = try movieContainer.decode(Double.self, forKey: .rating) overview = try movieContainer.decode(String.self, forKey: .overview) } } 
Manajer jaringanBuat file 
NetworkManager di folder Manajer. Saat ini, NetworkManager hanya berisi dua properti statis: kunci API dan enumerasi yang menjelaskan jenis server yang akan dihubungkan. 
NetworkManager juga berisi 
Router yang bertipe 
MovieApi .
 Respon jaringan
Respon jaringanBuat enumerasi NetworkResponse di NetworkManager.

Kami menggunakan enumerasi ini ketika memproses tanggapan terhadap permintaan dan kami akan menampilkan pesan yang sesuai.
HasilBuat enumerasi 
Hasil di NetworkManager.

Kami menggunakan 
Hasil untuk menentukan apakah permintaan itu berhasil atau tidak. Jika tidak, maka kami akan mengembalikan pesan kesalahan dengan alasan.
Meminta Respons PemrosesanBuat fungsi 
handleNetworkResponse . Fungsi ini mengambil satu argumen, seperti 
HTTPResponse, dan mengembalikan Hasil.

Dalam fungsi ini, tergantung pada kode status yang diterima dari HTTPResponse, kami mengembalikan pesan kesalahan atau tanda permintaan yang berhasil. Biasanya, kode dalam kisaran 200..299 berarti sukses.
Membuat permintaan jaringanJadi, kami telah melakukan segalanya untuk mulai menggunakan lapisan jaringan kami, mari kita coba membuat permintaan.
Kami akan meminta daftar film baru. Buat fungsi dan 
beri nama 
getNewMovies .

Mari kita lakukan langkah demi langkah:
1. Kami mendefinisikan metode 
getNewMovies dengan dua argumen: nomor halaman pagination dan handler penyelesaian, yang mengembalikan array opsional model 
Movie , atau kesalahan opsional.
2. Hubungi 
Router . Kami melewati nomor halaman dan 
menyelesaikan proses di penutupan.
3. 
URLSession mengembalikan kesalahan jika tidak ada jaringan atau tidak mungkin membuat permintaan dengan alasan apa pun. Harap dicatat bahwa ini bukan kesalahan API, kesalahan seperti itu terjadi pada klien dan biasanya terjadi karena kualitas koneksi Internet yang buruk.
4. Kita perlu memberikan 
tanggapan kepada 
HTTPURLResponse , karena kita perlu mengakses properti 
statusCode .
5. Nyatakan 
hasil dan inisialisasi dengan menggunakan metode 
handleNetworkResponse6. 
Sukses berarti permintaan itu berhasil dan kami menerima respons yang diharapkan. Kemudian kami memeriksa apakah data datang dengan jawabannya, dan jika tidak, maka kami cukup mengakhiri metode ini melalui pengembalian.
7. Jika jawaban datang dengan data, maka perlu mem-parsing data yang diterima ke dalam model. Setelah itu, kami meneruskan array model yang dihasilkan ke penyelesaian.
8. Dalam hal terjadi kesalahan, teruskan kesalahan tersebut sampai 
selesai .
Itu saja, ini adalah bagaimana lapisan jaringan kita sendiri bekerja pada Swift murni, tanpa menggunakan dependensi dalam bentuk pod dan pustaka pihak ketiga. Untuk membuat permintaan uji api untuk mendapatkan daftar film, buat MainViewController dengan properti 
NetworkManager dan panggil metode 
getNewMovies melaluinya.
  class MainViewController: UIViewController { var networkManager: NetworkManager! init(networkManager: NetworkManager) { super.init(nibName: nil, bundle: nil) self.networkManager = networkManager } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .green networkManager.getNewMovies(page: 1) { movies, error in if let error = error { print(error) } if let movies = movies { print(movies) } } } } 
Bonus kecilAnda mengalami situasi di Xcode ketika Anda tidak mengerti apa jenis placeholder yang digunakan di tempat tertentu? Misalnya, lihat kode yang baru saja kita tulis untuk 
Router .

Kami menentukan sendiri 
NetworkRouterCompletion , tetapi bahkan dalam kasus ini mudah untuk melupakan jenisnya dan bagaimana menggunakannya. Tapi Xcode tercinta kami mengurus semuanya, dan cukup dengan mengklik dua kali pada placeholder dan Xcode akan menggantikan tipe yang diinginkan.
 Kesimpulan
KesimpulanSekarang kami memiliki implementasi lapisan jaringan berorientasi protokol, yang sangat mudah digunakan dan yang selalu dapat Anda sesuaikan dengan kebutuhan Anda. Kami memahami fungsinya dan bagaimana semua mekanisme bekerja.
Anda dapat menemukan kode sumber di 
repositori ini .