Mengenal Arsitektur Kode VIPER dalam iOS App Development
Membangun Aplikasi yang Scalable
Bayangkan ketika kita sedang mengembangkan sebuah aplikasi, misalnya e-commerce besar seperti Tokopedia atau Shopee. Setiap layar memiliki fungsi-fungsinya tersendiri, misalnya daftar produk, pencarian produk, keranjang belanja, hingga proses pembelian. Seiring berjalannya waktu, fitur-fitur baru akan berdatangan dan meningkatkan kompleksitas sistem. Tanpa sadar, kode yang kita buat sudah berisikan ratusan baris kode yang sulit dipahami. Inilah yang sering disebut dengan istilah “Massive View Controller problem” di dalam dunia iOS development. Sehingga muncul pertanyaan: “Bagaimana cara menjaga agar struktur aplikasi tetap bersih, terorganisir, dan mudah dikembangkan dalam jangka waktu panjang?”.
Viper Architecture

Gambar 1. Arsitektur VIPER
Untuk menjawab masalah tersebut, hadir sebuah arsitektur yang bernama VIPER – View, Interactor, Presenter, Entity, dan Router. VIPER menerapkan konsep clean architecture dengan memisahkan logika bisnis, antarmuka pengguna, serta navigasi ke dalam lima komponen yang saling terhubung namun tetap independen.
Arsitektur VIPER pertama kali dipopulerkan oleh komunitas iOS developer yang menginginkan versi “lebih terstruktur” dari arsitektur MVC (Model View Controller) ataupun MVVM (Model View ViewModel). Berbeda dari kedua pola tersebut, arsitektur VIPER benar-benar memisahkan setiap lapisan sehingga setiap bagian kode memiliki tanggung jawab yang jelas, terorganisir, dan mudah untuk dikembangkan tanpa mengganggu bagian yang lain.
Arsitektur VIPER sangat cocok digunakan untuk aplikasi dengan skala besar, misalnya seperti E-Commerce App, aplikasi perbankan, atau aplikasi media sosial. Arsitektur ini juga cocok untuk perusahaan yang memiliki tim pengembang cukup banyak yang menangani modul berbeda secara paralel.
Agar lebih jelas mengenal VIPER, yuk kita lihat contoh implementasi VIPER dalam kasus pengembangan aplikasi e-commerce.
View
View bertugas menampilkan antarmuka pengguna dan menangani interaksi dasar seperti tap, scroll, atau input. Di dalam View tidak terdapat logika bisnis. Seluruh logika tersebut akan menjadi tanggung jawab Presenter. Tujuannya agar tampilan tetap ringan dan mudah diubah tanpa memengaruhi bagian lain dari aplikasi.
protocol ProductListViewProtocol: AnyObject {
func showProducts(_ products: [ProductEntity])
func showError(_ message: String)
}
class ProductListViewController: UIViewController, ProductListViewProtocol {
var presenter: ProductListPresenterProtocol?
private var products: [ProductEntity] = []
override func viewDidLoad() {
super.viewDidLoad()
title = “Product List”
presenter?.fetchProducts()
}
func showProducts(_ products: [ProductEntity]) {
self.products = products
}
func showError(_ message: String) {
print(“Error: \\(message)”)
}
}
ProductListViewController menampilkan daftar produk dari Presenter melalui fungsi presenter?.fetchProducts(). View hanya tahu bahwa Presenter yang menyediakan data, tanpa peduli dari mana data tersebut didapatkan dan bagaimana cara mendapatkannya.
Interactor
Interactor bertanggung jawab untuk menangani logika bisnis utama aplikasi. Proses seperti pengambilan data dari API, memproses hasil, atau menyimpan data ke database, merupakan pekerjaan yang biasa dilakukan oleh Interactor. Bagian ini merupakan “otak” di balik sebuah fitur pada aplikasi, memastikan semua proses bisnis dijalankan dengan benar. Interactor menerima perintah dari Presenter, memproses perintah tersebut, dan mengembalikan hasilnya kembali.
protocol ProductListInteractorProtocol {
func getProducts()
}
class ProductListInteractor: ProductListInteractorProtocol {
var presenter: ProductListPresenterOutputProtocol?
func getProducts() {
let products = [
ProductEntity(id: 1, name: “iPhone 16 Pro”, price: 1899.0),
ProductEntity(id: 2, name: “MacBook Air M4”, price: 1599.0)
]
presenter?.didFetchProducts(products)
}
}
ProductListInteractor **mengambil daftar produk dari sebuah tempat penyimpanan data (dalam case ini mock data), yang kemudian diteruskan ke Presenter memalui fungsi presenter?.didFetchProducts().
Presenter
Presenter berfungsi sebagai penghubung antara View dan Interactor. Presenter menerima aksi pengguna dari View, meminta data dari Interactor, dan memformat data agar siap untuk ditampilkan kembali di View. Presenter juga mengatur kapan navigasi atau perubahan tampilan perlu dilakukan.
protocol ProductListPresenterProtocol {
func fetchProducts()
}
protocol ProductListPresenterOutputProtocol {
func didFetchProducts(_ products: [ProductEntity])
}
class ProductListPresenter: ProductListPresenterProtocol, ProductListPresenterOutputProtocol {
weak var view: ProductListViewProtocol?
var interactor: ProductListInteractorProtocol?
var router: ProductListRouterProtocol?
func fetchProducts(){
interactor?.getProducts()
}
func didFetchProducts(_ products: [ProductEntity]){
if products.isEmpty{
view?.showError(“No products found.”)
} else {
view?.showProducts(products)
}
}
}
ProductListPresenter mengatur kapan data diminta dan bagaimana hasilnya dikirim kembali ke View. Fungsi interactor?.getProducts() menjalankan perintah pengambilan produk ke Interactor. Kemudian hasil yang diterima dari Interactor akan ditampilkan ke View dengan menjalankan fungsi view?.showProducts().
Entity
Entity mewakili struktur data aplikasi, biasanya berbentuk sebuah model atau objek yang berisikan informasi-informasi. Entity tidak berisikan perilaku atau behavior. Sehingga, komponen ini memastikan dan menjaga agar data tetap konsisten dan terpisah dari proses bisnis maupun tampilan.
struct ProductEntity {
let id: Int
let name: String
let price: Double
}
ProductEntity menyimpan informasi dasar terkait produk yang ada.
Router
Router mengatur alur navigasi antar layar atau modul dalam sebuah aplikasi. Setiap kali pengguna ingin berpindah layar, Router akan menentukan logika perpindahannya dan memastikan sistem menampilkan halaman yang benar.
protocol ProductListRouterProtocol {
func navigateToProductDetail(from view: ProductListViewProtocol, product: ProductEntity)
}
class ProductListRouter: ProductListRouterProtocol {
static func createModule() -> UIViewController {
let view = ProductListViewController()
let presenter = ProductListPresenter()
let interactor = ProductListInteractor()
let router = ProductListRouter()
view.presenter = presenter
presenter.view = view
presenter.interactor = interactor
presenter.router = router
interactor.presenter = presenter
return view
}
func navigateToProductDetail(from view: ProductListViewProtocol, product: ProductEntity) {
let detailVC = ProductDetailRouter.createModule(with: product)
if let sourceVC = view as? UIViewController {
sourceVC.navigationController?.pushViewController(detailVC, animated: true)
}
}
}
ProductListRouter bertanggung jawab untuk membangun seluruh modul View, Presenter, dan Interactor dari list produk melalui fungsi createModule(). Router juga bertanggung jawab untuk melakukan navigasi terhadap halaman detil produk melalui fungsi navigateToProductDetail().
Kekurangan VIPER
Walaupun menawarkan pembagian tanggung jawab yang jelas, arsitektur VIPER mempunyai beberapa kekurangan.
- Banyak Boilerplate Code: Untuk setiap fitur baru yang ditambahkan, tim pengembang perlu membuat lima file terpisah (View, Interactor, Presenter, Entity, dan Router). Hal ini bisa terasa berat untuk proyek dengan skala kecil atau tim dengan sumber daya yang terbatas.
- Waktu Pembelajaran: Developer yang terbiasa dengan arsitektur MVC atau MVVM akan memerlukan waktu ekstra untuk dapat memahami alur komunikasi VIPER. Perbedaan struktur yang signifikan menyulitkan proses migrasi dari arsitektur MVC atau MVVM ke VIPER.
- Kompleksitas yang Tinggi: Untuk fitur-fitur sederhana seperti halaman profil atau about page, penggunaan VIPER dapat dirasa terlalu “overkill”. Pembagian 5 file untuk fitur sederhana ini menjadikan waktu setup terasa lebih lama dibanding arsitektur yang lebih ringan. Developer juga perlu menjaga konsistensi antar protokol, memastikan seluruh file sudah terhubung satu dengan yang lain sesuai dengan tanggung jawabnya. Jika salah menghubungkan, Presenter atau Interactor bisa menyebabkan bug yang sulit dilacak oleh developer.
Kesimpulan
Arsitektur VIPER memberikan pembagian tanggung jawab yang jelas terhadap kode, menciptakan aplikasi yang bersih, modular, dan bisa dikembangkan lebih lanjut. Arsitektur ini sangat cocok digunakan untuk proyek dengan skala besar dengan sumber daya pengembang yang besar juga. Walaupun begitu, VIPER memiliki kekurangan seperti banyak boilerplate code dan membutuhkan waktu pembelajaran yang cukup karena kompleksitasnya yang sangat tinggi.
Penulis:
Muhamad Keenan Ario, S.Kom., M.Kom.
Referensi
- Gilbert, J., & Stoll, C. (n.d.). Architecting iOS Apps with VIPER. objc.io. Retrieved October 16, 2025, from https://www.objc.io/issues/13-architecture/viper/
- Nogoev, A. (2023, January 3). What is the best architecture for iOS app development? Custom Software Development Company. Retrieved October 16, 2025, from https://maddevs.io/blog/viper-architecture-for-ios-app-development/
- Klein, T. (2016, July 9). Building a reusable e-commerce framework using VIPER. Medium. Retrieved October 16, 2025, from https://medium.com/@Tibb0/building-a-reusable-e-commerce-framework-using-viper-d676a913eb11
- Bush, A. (n.d.). IOS System design – e-commerce app (Etsy example). The iOS Interview Guide. Retrieved October 16, 2025, from https://iosinterviewguide.com/system-design-interview/ecommerce
Last updated :
SOCIAL MEDIA
Let’s relentlessly connected and get caught up each other.
Looking for tweets ...