VIPER DESIGN PATTERN iOS

Mesut Aygun
3 min readJan 28, 2023

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 DESIGN PATTERN LAYERS

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..

--

--