Memahami Design Pattern VIPER Swift
Pola arsitektur menjadi suatu pendekatan metode yang umum digunakan oleh para software developer. Beberapa pola yang lumrah sekali dimanfaatkan seperti MVC (model-view-controller), MVP (model-view-presenter), dan MVVM (Model-View-ViewModel) menerapkan alur pengembangan yang cukup berbeda. Pola arsitektur (atau terkadang orang menyebut design pattern) seperti suatu hal yang wajib untuk diterapkan karena menyediakan kemudahan bagi para developer dalam membangun blue print alur pengembangan.
Pengembangan aplikasi berbasis iOS sendiri dapat memanfaatkan MVC untuk mengembangkan aplikasi iOS dengan skala kecil atau kompleksitas yang tidak rumit. Saat mengembangkan aplikasi yang diprediksi akan memiliki kompleksitas yang cukup rumit, beberapa pola umum.
Tentang VIPER
VIPER (View-Interactor-Presenter-Entity-Router) adalah pola arsitektur pengembangan iOS yang memisahkan setiap kode berdasarkan masing-masing fungsionalitasnya. Fungsionalitas pada VIPER diklasifikasi berdasarkan 5 class, yaitu :
-
View
Sesuai dengan namanya, view merepresentasikan apa yang akan dilihat olehpengguna aplikasi. Layouting dan data binding ditempatkan pada view. Data yang ditampilkan pada view diterima melalui presenter, selain itu view dapat melakukan transisi ke halaman lain atau routing melalui presenter kemudian diteruskan ke router.
-
Interactor
Interactor melakukan proses data yang diperoleh dari API kemudian disimpan pada Entity. Peran interactor sebagai komunikator antara aplikasi dengan backend. Data yang berhasil diperoleh akan diteruskan ke view melalui perantara presenter.
-
Presenter
Presenter berperan sebagai mediator atau penghubung antar view, router, maupun interactor. Bussiness logic pada aplikasi yang akan diimplementasi dilakukan pada presenter.
-
Entity
Kalau MVC, MVVC, MVP memiliki model yang merepresentasi struktur data, entity pada VIPER yang berperan sebagai model. Setelah interactor berhasil mendapatkan response data dari API, interactor mengolah data tersebut kemudian menyimpannya pada entity. Entity akan dibawa presenter menuju view untuk ditampilkan.
-
Router
Router pada VIPER berperan sebagai gerbang masuk dan keluarnya interaksi pengguna. Saat pengguna memasuki halaman, aplikasi harus melalui router, begitupun sebaliknya.
Protocol
Swift memiliki fitur protocol yang dapat diterapkan pada setiap class, enumeration, dan struct. Fungsi protocol sendiri dalam Swift membuat mekanisme atau prosedur untuk mendefinisikan method atau properties dalam sebuah class, enumeration, ataupun struct. Jadi, setiap class ataupun enum yang mengimplementasikan protocol harus menyesuaikan method atau properties yang sudah didefinisikan di dalam protocol.
VIPER memanfaatkan protocol untuk menyusun prosedur interaksi antar class. Peran protocol penting untuk menjaga konsistensi alur. Class VIPER harus terdiri dari method dan properties yang sudah didefinisikan saat membuat protocol.
Penyusunan protocol ini cukup sulit diselesaikan karena developer harus menggambarkan alur secara abstrak saat inisiasi project atau module. Tapi, setidaknya langkah awal ini mempermudah ketika developer mulai melakukan coding untuk tiap-tiap class.
Alur
Oke, mulai bahas ke bagian yang sulit untuk dijelaskan. Ketika memikirkan alur ini hal yang mungkin cukup membingungkan adalah ketika memikirkan dari mana mulainya.
Tapi, salah satu cara yang bisa dicoba adalah mulai dari bagian view. Kenapa view? Di dalam view kita bisa proyeksikan fungsi-fungsi interaksi langsugng dengan pengguna. Misalnya ketika membuat halaman yang menampilkan list artikel, maka susun protocol view-to-presenter dan presenter-to-view.
Disclaimer : bagian-bagian code ini hanya sebagai prototype semata, tidak bisa di-build secara mentah-mentah 🙇🏻♂️
View
// /Modules/Article/Protocol/ArticleFeedsProtocol.swift
import Foundation
import UIKit
protocol ViewToPresenterArticleFeedsProtocol: AnyObject {
var view: PresenterToViewArticleFeedsProtocol? {get set}
var interactor: PresenterToInteractorArticleFeedsProtocol? {get set}
var router: PresenterToRouterArticleFeedsProtocol? {get set}
func startFetchArticleFeeds()
func showDetailArticleFeeds(nc: UINavigationController)
}
protocol PresenterToViewArticleFeedsProtocol: AnyObject {
func articleListSuccessFetched()
func articleListFailedFetched()
}
Protocol ini nanti diimplementasikan ke class view, misalnya class *view-*nya seperti ini.
// /Modules/Article/View/ArticleFeedsViewController.swift
import UIKit
class ArticleFeedsViewController: UIViewController {
// TODO : Add IBOutlets, Properties, table view or other methods here...
var presenter: ViewToPresenterArticleFeedsProtocol?
override func viewDidLoad() {
super.viewDidLoad()
presenter?.startFetchArticleFeeds()
// Do any additional setup after loading the view.
}
}
extension ArticleFeedsViewController: PresenterToViewArticleFeedsProtocol {
func onArticleFeedsResponseSuccess() {
// TODO: reload table view if you use UITableView
}
func onArticleFeedsResponseFailed() {
// TODO: show error screen
}
}
Class ini berisi methods, properties, dan IBOutlets untuk menampilkan UI dari storyboard maupun XIB. viewDidLoad()
akan memanggil method dari class presenter untuk menjalankan proses pengambilan data yang nantinya interactor akan dieksekusi di dalam method tersebut.
Selain itu, pada class view ditambahkan extension yang mengimplementasi *protocol presenter-to-view*. Extension view ini akan mengeksekusi methods yang memproses data yang selesai diambil melalui interactor.
Presenter
Karena protocol view-to-presenter sudah dibuat saat membuat class view, maka selanjutnya hanya membuat protocol interactor-to-presenter. Protocol ini untuk menerima response atau data model yang telah berhasil di-mapping dari interactor.
// /Modules/Article/Protocol/ArticleFeedsProtocol.swift
protocol InteractorToPresenterArticleFeedsProtocol: AnyObject {
func articleListSuccessFetched()
func articleListFailedFetched()
}
Kemudian saatnya mengimplementasi *protocol view-to-presenter* dan interactor-to-presenter yang baru saja dibuat di class presenter. Di dalam class presenter, method startFetchArticleFeeds
memanggil method interactor yang berfungsi untuk melakukan fetch data. Selain itu, dari class presenter fungsi routing ke halaman lain bisa dipanggil dari method presenter.
Extension presenter mengimplementasi protocol interactor-to-presenter untuk meneruskan response data dari interactor ke view.
// /Modules/Article/Presenter/ArticleFeedsPresenter.swift
import Foundation
import UIKit
class ArticleFeedsPresenter: ViewToPresenterArticleFeedsProtocol {
var view: PresenterToViewArticleFeedsProtocol?
var interactor: PresenterToInteractorArticleFeedsProtocol?
var router: PresenterToRouterArticleFeedsProtocol?
func startFetchArticleFeeds() {
interactor?.fetchArticleFeeds()
}
func showDetailArticleFeeds(nc: UINavigationController) {
// TODO : Add action or router that routing to next module/view
}
}
extension ArticleFeedsPresenter: InteractorToPresenterArticleFeedsProtocol {
func articleListSuccessFetched() {
view?.onArticleFeedsResponseSuccess()
}
func articleListFailedFetched() {
view?.onArticleFeedsResponseFailed()
}
}
Interactor
Supaya presenter mampu meneruskan perintah proses fetching data dari view ke interactor, saatnya menambahkan *protocol presenter-to-interactor* dan class interactor yang berisi method fetching data.
// /Modules/Article/Protocol/ArticleFeedsProtocol.swift
protocol PresenterToInteractorArticleFeedsProtocol: AnyObject {
var presenter: InteractorToPresenterArticleFeedsProtocol? {get set}
var article: [ArticleFeeds]? {get}
func fetchArticleFeeds()
}
Class interactor berisi method yang berperan untuk melakukan tugas fetching data atau integrasi dengan API. Third party seperti Alomofire dapat dimanfaatkan untuk melakukan request dan menerima response dari service API. Kemudian di class ini juga instance dari Entity class diperlukan sebagai data model yang menyimpan response data.
// /Modules/Article/Interactor/ArticleFeedsInteractor.swift
import Foundation
import UIKit
class ArticleFeedsInteractor: PresenterToInteractorArticleFeedsProtocol {
var article: [ArticleFeeds]?
var presenter: InteractorToPresenterArticleFeedsProtocol?
func fetchArticleFeeds() {
// TODO : fetch data from some API using third library like Alamofire, keep response data to ArticleFeeds
}
}
Router
Class router mengimplementasi protocol presenter-to-router. Class router sendiri terdiri dari method untuk inisiasi modul melalui static method yang mengembalikan class view atau *view controller-*nya. Selain itu terdiri dari methods yang melakukan navigasi ke halaman lain. Berikut contohnya.
// /Modules/Article/Protocol/ArticleFeedsProtocol.swift
protocol PresenterToRouterArticleFeedsProtocol: AnyObject {
static func createArticleFeedsModule() -> ArticleFeedsViewController
func pushToArticleFeeds(nav: UINavigationController)
func pushToArticleDetail(nav: UINavigationController)
}
// /Modules/Article/Router/ArticleFeedsRouter.swift
import Foundation
import UIKit
class ArticleFeedsRouter: PresenterToRouterArticleFeedsProtocol {
static func createArticleFeedsModule() -> ArticleFeedsViewController {
let view = ArticleFeedsViewController()
let presenter = ArticleFeedsPresenter()
let interactor: PresenterToInteractorArticleFeedsProtocol = ArticleFeedsInteractor()
let router: PresenterToRouterArticleFeedsProtocol = ArticleFeedsRouter()
view.presenter = presenter
presenter.view = view
presenter.interactor = interactor
presenter.router = router
return view
}
func pushToDetailArticleFeeds(nav: UINavigationController) {
// TODO: call other module router to navigate other modules
}
}
Entity
Terakhir, entity yang terdiri dari class, struct, maupun enumeration sebagai data model dari module. Dalam file entity bisa dibuat beberapa class, enumeration, atau struct.
// /Modules/Article/Entity/ArticleFeedsEntity.swift
import Foundation
struct ArticleFeeds : Codable {
var title: String?
var author: String?
var body: String?
var publishDate: String?
}
Source
- https://medium.com/@smalam119/viper-design-pattern-for-ios-application-development-7a9703902af6
- https://docs.swift.org/swift-book/