Introduction
MVC (Model-View-Controller) adalah salah satu architecture pattern yang menjadi base dari projek baru dari iOS. Saya cukup yakin jika semua iOS developer tentunya tahu dengan pattern ini. Secara umum MVC terdiri 3 komponen yaitu:
- Model
Komponen atau layer ini berisi data model aplikasi, networking service, data persistence dll. Intinya komponen ini berisi logik yang berhubungan data yang ada pada aplikasi kita. - View
Untuk komponen ini sendiri pastinya kita sudah tahu yaitu Storyboard yang berfungsi untuk membuat view dari aplikasi kita. - Controller
Komponen ini biasanya kita sebut dengan ViewController, yang berfungsi menjadi jembatan antara model dan view. Di mana controller ini bertugas untuk memanggil data dan mengupdatenya ke dalam view, atau mengirim sebuah respon atau action ketika view seperti button, textfield dll mengalami perubahaan state.
Problem
Untuk architecture ini sendiri menurut saya sangat cocok untuk aplikasi yang masih sederhana, namun kenyataannya implementasi yang ada masih salah. Biasanya dalam project yang ada, kita selalu menempatkan semua logic yang ada dalam ViewController bukan? Biasanya untuk melakukan fetching data, maka kita memanggil Singleton ke dalam ViewController yang ada. Sehingga yang ada bukanlah MVC(Model-View-Controller), melainkan Massive View Controller.
Beberapa masalah yang terjadi:
- Menyalahi aturan Single Responsibility Principle di mana ViewController menghandle terlalu banyak operasi, ViewController seharusnya hanya bertugas untuk memanggil dan melakukan update.
- ViewController seharusnya tidak mengetahui implementasi detail dari operasi tersebut, dalam kasus ini kita telah menyalahi aturan dari Dependency Inversion Principle, “Module shoud not depend on Detail/Implementation (Concrete Class), it should depend on abstraction”. Dengan menyalahi aturan tersebut, class ViewController menjadi Tighly Couple dengan Singleton yang ada, dan menyebabkan kode yang ada susah untuk ditest.
Nah untuk mengatasi hal tersebut tidaklah susah, kita cukup memanfaatkan Dependency Injection dan mengikuti aturan Dependency Inversion. Ayo kita langsung hands-on aja.
Starter Project
https://github.com/windywu812/BetterMVC
Hands-On
Silahkan membuka branch Main, aplikasi ini ialah aplikasi sederhana yang memuat games dari API dan menampilkannya pada tableview yang ada. Struktur kode tersebut merupakan struktur kode yang sering kita lihat dalam beberapa project. Secara sekilas struktur kode yang ada sudah cukup rapi, namun masih dapat dikembangkan. Dalam kode ini, ViewController masih bergantung pada class NetworkService, sehingga ketika membuat test kita harus menguji implementasi yang asli (memanggil data langsung) yang biasanya memakan waktu cukup lama, padahal sebuah UnitTesting harus berjalan dengan cepat.
1. Mendefine sebuah Protocol
Membuat sebuah protocol, nantinya ViewController akan bergantung pada protocol ini (abstraction), bukan class NetworkService(Concrete Type) secara langsung.
2. Membuat class yang akan mengimplementasi protocol tersebut
Sekarang kode pada fungsi getAllGames pada class ViewController dapat dipindahkan ke dalam class GameImplementation ini.
3. Refactor kode pada class ViewController
Menambah sebuah dependency yaitu GameProtocol(Abstraction) tersebut dan menghandle completion dari fungsi getAllGames
Unit Test
Dengan membuat kode kita menjadi seperti ini, maka kita akan dapat melakukan test dengan mudah dan cepat.
1. Membuat Mock Object
Dengan membuat mock object, kita tidak perlu lagi melakukan test dengan real APICall. Kita dapat membuat data dummy kita sendiri
2. Write Unit Test
Karena ViewController bergantung dengan abstraction, kita dapat dengan mudah untuk mengganti dependency yang ada. Di sini kita tidak lagi menggunakan class GameImplementation, melainkan MockGameImplementation yang berisi data dummy kita.
Kesimpulan
Dengan membuat kode seperti ini, kita dapat membuat class/kode memiliki ruang untuk bernafas yaitu tidak bergantung pada class tertentu (Loose Coupling). Unit Test yang ditulis dapat dijalankan dengan cepat dan secara offline. Jika kita mengetest dengan memanggil API Call secara langsung, maka beberapa masalah yang dapat terjadi ialah:
- Masalah Internet yang sangat mempengaruhi kecepatan Unit Testing kita, sehingga hasil yang ada tidak konsisten. Apalagi jika kita berada di jangkauan yang tidak memiliki sinyal internet, maka kita tidak dapat melakuakan test. Test yang ada juga relative cepat yaitu hanya memerlukan waktu sekitar 0.1 detik, jika dibandingkan dengan ApiCall yang asli maka akan membutuhkan waktu beberapa detik.
- Kendala terhadap API Call limit pada beberapa API, sehingga akan menghambat proses development yang ada
Untuk melihat hasil akhir, Anda bisa checkout ke dalam branch Final. Semoga dapat memberikan insight baru dan dapat bermanfaat bagi teman-teman.