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