Merhaba, hazırlamış olduğum bu yazıda VIPER mimarisinin ne olduğu, neden tercih edilmesi gerektiği, avantajları ve dezavantajları hakkında bilgilere yer verdim.
VIPER Nedir?
VIPER, uygulamamızı bir modül içerisinde, farklı özelliklere sahip beş katmana ayıran bir mimaridir. Her bir katmanın ayrı bir rolü bulunmaktadır. Bunları aşağıdaki gibi özetleyebiliriz:
View: View, çoğu zaman dokunma olaylarını alan dummy bir nesnedir. MVC’nin binlerce kod satırı içeren devasa ViewController’ı yerine, yapılacak işlemler ile ilgili tüm kodlar ve karar verme işlemleri View’de bulunmamalıdır. Örneğin, kullanıcıdan bir dokunma işlemi alındığında, View nesnesi Presenter’a bu durumu bildirmelidir.
Interactor: Interactor business logic içerir. Ve çoğunlukla API çağrılarından sorumludur. Bu katmanda yapılan işlemler UI dan bağımsız olarak gerçekleşir.
Presenter: Presenter, bu mimarinin merkezindeki katman diyebiliriz. Diğer katmanlarla haberleşmeyi sağlar. Bütün kararlar presenter katmanında ele alınır. View’dan gelen bildirimleri alır ve onlarla ne yapılacağına karar verir. Örneğin, Interactor’dan bazı veriler isteyebiliriz veya Router’a farklı bir ekrana yönlenmesini söyleyebiliriz. Presenter ayrıca, Interactor’dan veriler alır ve komutlarla View’a iletir.
Entity: Entity, Interactor tarafından yönlendirilen veri objeleridir.
Router: Router’ın işi ekranlar arasında yönlenmeyi sağlamaktır. Yalnızca Presenter’ı tanır ve ondan komutlar alır.
İşleyiş
- View, Presenter ile haberleşir. Kullanıcının gerçekleştirdiği aksiyonu Presenter’a iletir. Presenter’dan gelen cevaba göre işlemi gerçekleştirir.
- Interactor, Presenter ile haberleşir. Servis ile haberleşme de Interactor’da yapılır.
- Router, presenter ile haberleşir. Ve ekran yönlenmeleri sadece Router’da yapılır.
- Katmanlar arası Protocol’ler ile iletişim kurulması sağlanır.
Projemiz büyüdüğünde binlerce satıra sahip ViewContoller olması durumunda, global değişkenlerin gittikçe artması ve uygulamayı debug etmenin gittikçe zorlaşması durumunda VIPER tasarım desenini tercih edebiliriz.
Avantajlar
- Her bir nesnenin tek bir rolü olması
- Veri akışının kolay takip edilebilmesi
- Büyük projelerde hata ayıklamanın kolay olması ve test kolaylığı
Dezavantajlar
- Küçük projelerde karmaşık olması
- Yeni bir geliştiricinin VIPER’ ın temellerini öğrenmesi için normalden fazla zaman harcaması gerekmesi
Örnek Uygulamaya Giriş
Uygulamamız ekranda ‘Hello VIPER’ metni göstermek üzerine kurgulandı. Öncelikle projemizi oluşturup katmanlarımızı hazır hale getirmeliyiz. Aşağıdaki gibi bir klasörleme yapısı oluşturabiliriz.
Presenter’ı mimarinin merkezinde düşünebiliriz. Böylece yapı kafamızda daha rahat canlanabilir. Entity hariç her bir katmanın Protocol’ü bulunur. Bu protocol’ler ile katmanlar arası iletişim kurulur.
Protocol’ler oluşturulduktan sonra hangi katmanla iletişim kurulması gerektiği göz önünde bulundurularak her katman ile ilgili değişkenler tanımlanır.
Ardından, Modül oluşturma methodu yazmamız gerekmektedir. Genel kullanım olarak; farklı modüller Router ile iletişim kurduğu için bu methodu Router içerisine yazmamız daha uygun olacaktır.
HomeRouter.swift
“createModule” fonksiyonu yukarıda bahsedildiği gibi Router içerisinde oluşturuldu.
import UIKit
protocol HomeRouterInterface {
}
class HomeRouter {
func createModule() -> UIViewController {
let view = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "HomeViewController") as! HomeViewController
let interactor = HomeInteractor()
let router = HomeRouter()
let presenter = HomePresenter(view: view, interactor: interactor, router: router)
view.presenter = presenter
return view
}
}
extension HomeRouter: HomeRouterInterface {
}
ViewController’da Label oluşturuldu, updateTitle fonksiyonu yazıldı. Burada amaç Interactor içerisinde oluşturulan getTitle methodunun nasıl çalıştığını görüp ardından updateTitle methodundan gelen değerin ekrana nasıl basıldığını görmek.
HomeViewController.swift
import UIKit
protocol HomeViewControllerInterface: class {
func updateTitle()
}
class HomeViewController: UIViewController {
@IBOutlet var helloLabel: UILabel!
var presenter: HomePresenterInterface!
override func viewDidLoad() {
super.viewDidLoad()
self.presenter.viewDidLoad()
}
}
extension HomeViewController: HomeViewControllerInterface {
func updateTitle() {
helloLabel.text = "Hello VIPER"
}
}
HomePresenter.swift
import UIKit
protocol HomePresenterInterface {
func viewDidLoad()
}
class HomePresenter {
weak var view: HomeViewControllerInterface?
var interactor: HomeInteractorInterface
var router: HomeRouterInterface
init(view: HomeViewControllerInterface, interactor: HomeInteractorInterface, router: HomeRouterInterface) {
self.view = view
self.interactor = interactor
self.router = router
}
}
extension HomePresenter: HomePresenterInterface {
func viewDidLoad() {
let homeModel = self.interactor.getTitle()
print("Home Model value is \(homeModel)")
view?.updateTitle()
}
}
View, Presenter ile haberleştiği için View içerisinde presenter değişkeni oluşturuldu.
Presenter içerisinde View’da bulunan viewDidLoad fonksiyonu eklendi. Ayrıca view, router ve interactor değişlenleri tanımlandı.
– Ekran açıldığında viewDidLoad çalışacak presenter’a gidilecek.
– Presenter’da kullanıcıya gösterilecek title için getTitle ve updateTitle methoduna gidilecek. getTitle methodu Interactor’da tanımlanmıştır. Title’ın ne olacağı ilk olarak Interactor’da belirlenmiştir.
– Presenter Interactor’da oluşturulan title değerini alacak ardından bunu çağıracak.
– Title ın nasıl güncellendiğini görebilmemiz için de Presenter updateTitle methodunu çağıracak. Ve ekranda ne yazacağını View’a haber verecek. Router da hangi ekrana yönlenileceğini belirleyecek.
– Son olarak Presenter tüm bu bilgileri View’a haber verecek ve kullanıcı ekranda verilen mesajı görebilecek.
HomeInteractor.swift
import Foundation
protocol HomeInteractorInterface {
func getTitle() -> HomeModel
}
class HomeInteractor { }
extension HomeInteractor: HomeInteractorInterface {
func getTitle() -> HomeModel {
return HomeModel(title: "Home VIPER")
}
}
HomeModel.swift
struct HomeModel {
let title: String
}
AppDelegate.swift dosyasındaki application fonksiyonunun içerisine aşağıdaki gibi tanımlamamızı yapıyoruz. Uygulama açıldığında gösterilecek olan ViewController’ ımızın hangisi olacağını burada belirtiyoruz.
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = HomeRouter().createModule()
self.window?.makeKeyAndVisible()
Sonuç
GetTitle methoduyla; title ‘Home VIPER’ olarak alınmıştır.
updateTitle methodu çağırıldığında; title ‘Hello VIPER’ olmuştur.
Output
Tebrik ederim Gamze güzel yazı olmuş. Devamını beklerim.