VIPER DESIGN PATTERN iOS
Yazılım mimarileri kod tekrarını en aza indiren, uygulama geliştirirken tekrar eden durumlar için genel bir çözüm sağlayan ortak bir kod yazma yöntemini standartlaştıran tekniklerdir.
Bu yazıda iOS geliştirme için VIPER mimarisinden elimden geldiğince bahsetmeye çalışacağım.
Viper, delegate odaklı bir mimaridir. Dolayısıyla, farklı katmanlar arasındaki iletişimin çoğu delegate yoluyla yürütülür. Bir katman diğerini bir protokol aracılığıyla çağırır. Çağıran katman, bir protokolden bir işlev çağırır. Dinleme katmanı bu protokole uyar ve işlevi uygular. Şimdi VIPER mimarisi katmanlarına bakalım ;
View
Uygulama arayüzünü kullanıcıya göstermek ve yanıtlarını islem yaptığımız sınıf örneğin adımızın yazdığı bir label. Bir başka deyişle kullanıcı arayüzü elemanlarını içeren katmanımızdır.
Interactor
Bu katman presenter katmanıyla iletişime giren katmandır. Presenterdan delegate yöntemiyle aldığı görevlerini yerine getirir. İşlemin yapıldığı katmandır. Örneğin view katmanında 2 ile 2 nin toplama işlemini gösteren bir görev olsun işlemin yapıldığı katman burasıdır.
Presenter
Tüm katmanlarla iletişim kurabilen katmandır. View katmanından kullanıcı etkileşimini alır. Interactor katmanına iletir. Router ile iletişime girerek tüm sayfa yönlendirmeleri yapılır.
Entity
Bu katmanda sadece modellerimiz bulunur. Bunlar class ya da struct olan yapılarımızdır.
Router
Katmanların iletişime geçmesini sağlayan Protocollerin tanımlandığı ve sayfa geçişlerinin oluşturulduğu katmandır. Presenter ile etkileşim halinde hangi ekranın yönlendirilmesi yapılacağını belirler. Protocollerde yaptığımız sınıf yetkilendirme işlemleri burada yapılır.
Unutmamız gereken şey bu katmanlar arası iletişim Protokoller aracılığı ile yapılıyor.
Peki neden VIPER, avantajları neler ;
- Gelecekte daha fazla soyutlama ve dolayısıyla daha fazla ölçeklenebilirlik seçeneği.
- Daha kolay test edilebilirlik ve daha güvenilir ve hataya daha az eğilimlidir.
- Geliştirmenin ilk aşamaları yavaş olabilse de, akışı anladıktan sonra diğer mimarileri uygulamak kadar kolay ve hızlıdır.
- Tek sorumluluk ilkesi sayesinde geliştiricilerin neyin nerede olduğunu anlaması daha kolay.
Dezavantajlari ise ;
- İlk başta cok karmaşık gelebilir.
- Hantaldır. Çok fazla katman var.
- Küçük projeler icin önerilmez.
- SWIFTUI ile yazılmış basit ve daha küçük uygulamalar için kullanılması tavsiye edilmez ama UIKit ile çok kompleks ve büyük projeler için kullanılabilir.
Küçük bir uygulama örneği ile konuyu daha iyi anlamaya çalışalım. Örnekte iki sayının toplamını ekranda gösteren uygulamayı VIPER mimarisi ile oluşturmayı deneyelim.
View katmanında kullanıcıdan iki sayı aldık. View to Presenter protokolu ile veriyi presentera gönderdik.
class MainViewController: UIViewController {
@IBOutlet weak var labelResult: UILabel!
@IBOutlet weak var textNum1: UITextField!
@IBOutlet weak var textNum2: UITextField!
var presenterObj:ViewToPresenterProtocol?
/*
protocol ViewToPresenterProtocol {
var interactor:PresenterToInteractorProtocol? {get set}
var view:PresenterToViewProtocol? {get set}
func toplamaYap(sayi1:String,sayi2:String)
}
*/
override func viewDidLoad() {
super.viewDidLoad()
labelResult.text = "0"
Router.createModule(ref: self)
}
@IBAction func buttonToplama(_ sender: Any) {
if let sayi1 = textNum1.text,let sayi2 = textNum2.text {
presenterObj?.toplamaYap(sayi1: sayi1, sayi2: sayi2)
}
}
}
extension MainViewController : PresenterToViewProtocol {
/*
protocol PresenterToViewProtocol {
func viewSendData(sonuc:String)
}
*/
func viewSendData(sonuc: String) {
labelResult.text = sonuc
}
}
Presenter view den aldığı veriyi işlem yaptırmak üzere interactor katmanına protokol aracılığı ile gönderir.
class Presenter : ViewToPresenterProtocol {
var interactor: PresenterToInteractorProtocol?
var view: PresenterToViewProtocol?
func toplamaYap(sayi1: String, sayi2: String) {
interactor?.toplama(sayi1: sayi1, sayi2: sayi2)
}
}
extension Presenter : InteractorToPresenterProtocol {
func presenterSendData(sonuc: String) {
view?.viewSendData(sonuc: sonuc)
}
}
Interactorda ise işlemler yapılıp tekrar presenter katmanına oradan view katmanına gönderilir.
class Interactor : PresenterToInteractorProtocol {
var presenter: InteractorToPresenterProtocol?
func toplama(sayi1: String, sayi2: String) {
if let s1 = Int(sayi1),let s2 = Int(sayi2) {
let toplam = s1 + s2
presenter?.presenterSendData(sonuc: String(toplam))
}
}
}
Protokollerimizin olduğu dosya
protocol PresenterToInteractorProtocol {
var presenter:InteractorToPresenterProtocol? {get set}
func toplama(sayi1:String,sayi2:String)
}
protocol ViewToPresenterProtocol {
var interactor:PresenterToInteractorProtocol? {get set}
var view:PresenterToViewProtocol? {get set}
func toplamaYap(sayi1:String,sayi2:String)
}
protocol InteractorToPresenterProtocol {
func presenterSendData(sonuc:String)
}
protocol PresenterToViewProtocol {
func viewSendData(sonuc:String)
}
protocol PresenterToRouterProtocol {
static func createModule(ref:MainViewController)
}
Tüm yetkilendirme ve yönlendirmelerin yapıldığı Router katmanı
class Router : PresenterToRouterProtocol {
static func createModule(ref: MainViewController) {
let presenter = Presenter()
//View layer
ref.presenterObj = presenter
//Presenter layer
ref.presenterObj?.interactor = Interactor()
ref.presenterObj?.view = ref
//Interactorlayer
ref.presenterObj?.interactor?.presenter = presenter
}
}
Karmaşık gözüksede protokollerin mantığını öğrenip pratik yaptıkça daha kolay hale geliyor umarım sizin için faydalı bir yazı olmuştur bir sonraki yazıda görüşmek üzere..