Halo pembaca!
Pada artikel ini saya akan berbicara tentang arsitektur aplikasi iOS - 
Clean Swift . Kami akan mempertimbangkan poin teoritis utama dan menganalisis contoh dalam praktik.

Teori
Untuk memulai, kita akan menganalisis terminologi dasar arsitektur. Di 
Bersihkan Swift, aplikasi terdiri dari adegan, mis. setiap layar aplikasi adalah satu adegan. Interaksi utama dalam adegan melalui loop berurutan antara komponen 
ViewController -> 
Interactor -> 
Presenter . Ini disebut siklus 
VIP .
Jembatan antara komponen adalah file 
Model , yang menyimpan data yang dikirim. Ada juga 
Router yang bertanggung jawab untuk mentransfer dan mentransfer data antara adegan, dan 
Worker , yang mengambil alih bagian dari logika 
Interactor'a .

Lihat
Storyboard, XIB, atau elemen UI ditulis melalui kode.
ViewController
Hanya bertanggung jawab untuk konfigurasi dan interaksi dengan 
View . Pengontrol tidak boleh berisi logika bisnis, interaksi jaringan, komputasi, dan sebagainya.
Tugasnya adalah untuk memproses acara dengan 
Lihat , tampilkan atau kirim data (tanpa pemrosesan dan pemeriksaan) di 
Interactor .
Interactractor
Ini berisi logika bisnis tempat kejadian.
Ini bekerja dengan modul jaringan, basis data dan perangkat.
Interactor menerima permintaan dari 
ViewController (dengan data atau kosong), memprosesnya dan, jika perlu, mentransfer data baru ke 
Presenter .
Presenter
Dia terlibat dalam persiapan data untuk ditampilkan.
Sebagai contoh, tambahkan topeng ke nomor telepon atau buat huruf pertama di judul modal.
Ini memproses data yang diterima dari 
Interactor , dan kemudian mengirimkannya kembali ke 
ViewController .
Model
Seperangkat struktur untuk mentransfer data antara komponen-komponen dari siklus 
VIP . Setiap lingkaran siklus memiliki 3 jenis struktur:
- Permintaan - Struktur data (teks dari TextField, dll.) Untuk ditransfer dari ViewController ke Interactor
- Respons - Struktur dengan data (diunduh dari jaringan, dll.) Untuk ditransfer dari Interactor ke Presenter
- ViewModel - Struktur dengan data yang diproses (pemformatan teks, dll.) Di Presenter untuk mentransfer kembali ke ViewController
Pekerja
Membongkar 
Interactor , mengambil bagian dari logika bisnis aplikasi jika 
Interactor berkembang pesat.
Anda juga dapat membuat 
Pekerja yang umum untuk semua adegan, jika fungsinya digunakan di beberapa adegan.
Sebagai contoh, di 
Worker, Anda bisa membuat logika bekerja dengan jaringan atau database.
Router
Semua logika yang bertanggung jawab untuk transisi dan transfer data antara adegan diambil di 
Router .
Untuk memperjelas gambar siklus 
VIP , saya akan memberikan contoh standar - otorisasi.
- Pengguna memasukkan nama pengguna dan kata sandinya, mengklik tombol otorisasi
- ViewController memicu IBAction , setelah itu struktur dibuat dengan data pengguna dimasukkan ke dalam TextFields, (Model -> Permintaan)
- Struktur yang dibuat diteruskan ke metode fetchUser di Interactor'e
- Interactor mengirimkan permintaan ke jaringan dan menerima tanggapan tentang keberhasilan otorisasi
- Berdasarkan data yang diterima, buat struktur dengan hasil (Model -> Respons) dan diteruskan ke metode presentUser di Presenter'e
- Presenter memformat data sesuai kebutuhan dan mengembalikannya (Model -> ViewModel) ke metode displayUser di ViewController'e
- ViewController menampilkan data yang diterima kepada pengguna. Dalam kasus otorisasi, kesalahan dapat ditampilkan atau transisi ke adegan lain dapat dipicu menggunakan Router
Dengan demikian, kami mendapatkan struktur tunggal dan konsisten, dengan distribusi tanggung jawab menjadi komponen.
Berlatih
Sekarang mari kita lihat contoh praktis kecil yang menunjukkan bagaimana siklus 
VIP berjalan. Dalam contoh ini, kami akan mensimulasikan pemuatan data saat membuka adegan (layar). Saya menandai bagian utama dari kode dengan komentar.
Seluruh siklus 
VIP terkait dengan protokol, yang menyediakan kemampuan untuk mengganti modul apa pun tanpa mengganggu aplikasi.
Protokol 
DisplayLogic dibuat untuk 
ViewController , tautan yang diteruskan ke 
Presenter untuk dipanggil nanti. Untuk 
Interactor , dua protokol 
BusinessLogic dibuat, yang bertanggung jawab untuk memanggil metode dari 
ViewController , dan 
DataSource , untuk menyimpan data dan mentransfer adegan lain melalui 
Router ke 
Interactor . Presenter berlangganan protokol 
PresentationLogic untuk panggilan dari 
Interactor . Elemen penghubung dari semua ini adalah 
Model . Ini berisi struktur dengan bantuan pertukaran informasi antara komponen-komponen dari siklus 
VIP . Kami akan memulai analisis kode dengan itu.

Model
Pada contoh di bawah ini, untuk adegan 
Home , saya membuat file 
HomeModels yang berisi serangkaian pertanyaan untuk loop 
VIP .
Permintaan 
FetchUser akan bertanggung jawab untuk memuat data pengguna, yang akan kami pertimbangkan lebih lanjut.
|  | // Models | 
|  | /// VIP | 
|  | enum HomeModels { | 
|  |  | 
|  | /// VIP | 
|  | enum FetchUser { | 
|  |  | 
|  | /// Interactor View Controller | 
|  | struct Request { | 
|  | let userName: String | 
|  | } | 
|  |  | 
|  | /// Presentor Interactor | 
|  | struct Response { | 
|  | let userPhone: String | 
|  | let userEmail: String | 
|  | } | 
|  |  | 
|  | /// View Controller Presentor | 
|  | struct ViewModel { | 
|  | let userPhone: String | 
|  | let userEmail: String | 
|  | } | 
|  | } | 
|  | } | 
ViewController
Ketika kelas diinisialisasi, kami instantiate kelas 
Interactor dan 
Presenter adegan ini dan membangun dependensi di antara mereka.
Lebih jauh di 
ViewController'e hanya ada tautan ke 
Interactor . Dengan menggunakan tautan ini, kami akan membuat permintaan ke metode 
fetchUser (request :) di 
Interactor untuk memulai siklus 
VIP .
Di sini perlu diperhatikan bagaimana permintaan kepada 
Interactor terjadi. Dalam metode 
loadUserInfromation () , kami membuat turunan dari struktur 
Permintaan , tempat kami meneruskan nilai awal. Itu bisa diambil dari 
TextField , tabel, dan sebagainya. Sebuah instance dari struktur 
Permintaan diteruskan ke metode 
fetchUser (request :) , yang ada dalam protokol 
BusinessLogic dari 
Interactor kami.
|  | // ViewController | 
|  | /// | 
|  | protocol HomeDisplayLogic: class { | 
|  |  | 
|  | /// | 
|  | func displayUser(_ viewModel: HomeModels.FetchUser.ViewModel) | 
|  | } | 
|  |  | 
|  | final class HomeViewController: UIViewController { | 
|  |  | 
|  | /// Interactor'a | 
|  | var interactor: HomeBusinessLogic? | 
|  |  | 
|  | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { | 
|  | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) | 
|  | setup() | 
|  | } | 
|  |  | 
|  | required init?(coder aDecoder: NSCoder) { | 
|  | super.init(coder: aDecoder) | 
|  | setup() | 
|  | } | 
|  |  | 
|  | /// | 
|  | private func setup() { | 
|  | // VIP | 
|  | let interactor = HomeInteractor() | 
|  | let presenter = HomePresenter() | 
|  |  | 
|  | // | 
|  | interactor.presenter = presenter | 
|  | presenter.viewController = self | 
|  |  | 
|  | // Interactor View Controller | 
|  | self.interactor = interactor | 
|  | } | 
|  |  | 
|  | override func viewDidLoad() { | 
|  | super.viewDidLoad() | 
|  |  | 
|  | // | 
|  | fetchUser() | 
|  | } | 
|  |  | 
|  | /// Interactor | 
|  | private func loadUserInfromation() { | 
|  | // Interactor | 
|  | let request = HomeModels.FetchUser.Request(userName: "Aleksey") | 
|  |  | 
|  | // Interactor'a | 
|  | interactor?.fetchUser(request) | 
|  | } | 
|  | } | 
|  |  | 
|  | /// HomeDisplayLogic | 
|  | extension HomeViewController: HomeDisplayLogic { | 
|  |  | 
|  | func displayUser(_ viewModel: HomeModels.FetchUser.ViewModel) { | 
|  | print(viewModel) | 
|  | } | 
|  | } | 
Interactractor
Sebuah instance dari kelas 
Interactor berisi tautan ke protokol 
PresentationLogic , di mana 
Presenter ditandatangani.
Metode 
fetchUser (request :) dapat berisi logika pemuatan data apa pun. Misalnya, saya baru saja membuat konstanta, dengan data yang seharusnya diperoleh.
Dalam metode yang sama, sebuah instance dari struktur 
Response dibuat dan diisi dengan parameter yang diperoleh sebelumnya. 
Respons diteruskan ke 
PresentationLogic menggunakan metode 
presentUser (response :) . Dengan kata lain, di sini kami mendapatkan data mentah dan meneruskannya ke 
Presenter untuk diproses.
|  | // Interactor | 
|  | /// Interactor'a | 
|  | protocol HomeBusinessLogic: class { | 
|  |  | 
|  | /// | 
|  | func fetchUser(_ request: HomeModels.FetchUser.Request) | 
|  | } | 
|  |  | 
|  | final class HomeInteractor: HomeBusinessLogic { | 
|  |  | 
|  | /// | 
|  | var presenter: HomePresentationLogic? | 
|  |  | 
|  | func fetchUser(_ request: HomeModels.FetchUser.Request) { | 
|  | // | 
|  | // | 
|  | let userPhone = "+7 (999) 111-22-33" | 
|  | let userEmail = "im@alekseypleshkov.ru" | 
|  | // ... | 
|  | // Presentor' | 
|  | let response = HomeModels.FetchUser.Response(userPhone: userPhone, userEmail: userEmail) | 
|  |  | 
|  | // Presentor' | 
|  | presenter?.presentUser(response) | 
|  | } | 
|  | } | 
Presenter
Ini memiliki tautan ke protokol 
DisplayLogic , di mana 
ViewController ditandatangani. Itu tidak mengandung logika bisnis, tetapi hanya memformat data yang diterima sebelum menampilkannya. Dalam contoh ini, kami memformat nomor telepon, menyiapkan instance dari struktur 
ViewModel , dan meneruskannya ke 
ViewController menggunakan metode 
displayUser (viewModel :) dalam protokol 
DisplayLogic , di mana data sudah ditampilkan.
|  | /// | 
|  | protocol HomePresentationLogic: class { | 
|  |  | 
|  | /// Interactor'a | 
|  | func presentUser(_ response: HomeModels.FetchUser.Response) | 
|  | } | 
|  |  | 
|  | final class HomePresenter: HomePresentationLogic { | 
|  |  | 
|  | /// View Controller'a | 
|  | weak var viewController: HomeDisplayLogic? | 
|  |  | 
|  | func presentUser(_ response: HomeModels.FetchUser.Response) { | 
|  | // | 
|  | let formattedPhone = response.userPhone.replacingOccurrences(of: "-", with: " ") | 
|  |  | 
|  | // ViewModel View Controller | 
|  | let viewModel = HomeModels.FetchUser.ViewModel(userPhone: formattedPhone, userEmail: response.userEmail) | 
|  |  | 
|  | // View Controller'a | 
|  | viewController?.displayUser(viewModel) | 
|  | } | 
|  | } | 
Kesimpulan
Dengan arsitektur ini, kami mendapat kesempatan untuk mendistribusikan tanggung jawab, meningkatkan kenyamanan pengujian aplikasi, memperkenalkan penggantian bagian individu dari implementasi dan standar untuk menulis kode untuk bekerja dalam tim.
Terima kasih sudah membaca sampai akhir.
Seri artikel
- Memahami Arsitektur Swift Bersih (You Are Here)
- Router dan Passing Data dalam Arsitektur Swift Bersih
- Pekerja Arsitektur Swift Bersih
- Pengujian unit dalam arsitektur Clean Swift
- Contoh arsitektur toko online sederhana Clean Swift
Semua komponen adegan: 
TautanBantuan dalam menulis artikel: 
Bastien