Delegates and Protocols in Swift

Apple cihazları için uygulama geliştirirken mutlaka bilmemiz gereken Delegates and Protocols in Swift konusunu ele alıyoruz.

Delegation 2 sınıf arasında tek yönlü bilgi aktarımı ve haber verme mekanizması olarak çalışan bir kalıptır.

Adından da anlaşılacağı üzere bir “delege” işi söz konusu.

 

Delegation Nedir?

delegates and protocols in swift

Delegation demek bir işi birisine atamak anlamına gelir.

Örneğin bir şirket yönetiyorsunuz ve yapacak bir sürü işiniz var. Siz de diyorsunuzki “Ahmet benim işim başımdan aşkın. Gel şu işi de sen yap, bana yardım et.”

Benzer olarak işçi sendikalarını örnek verebiliriz. Bu sendikaların görevlendirdiği delegeler vardır. Delege olarak görev alan insanlar toplantılara katılır ve bağlı bulunduğu sendikanın vermiş olduğu görevleri yerine getirir, sendika için çalışır.

Apple da aynı bu mantık ile Delegation & Protocol kalıbını oluşturmuş. Biz de kodumuzu yazarken bütün işi View Controller a yaptırmayız. View Controller sınıfının yapacağı işler vardır. Bir de delege ettiği işler vardır.

 

Örnek: TableViewController

Bunun en bariz örneği TableViewController delege metodlarıdır. Örneğin yemek çeşitlerini listeleyip ve yönettiğimiz ListViewController isminde bir sınıfımız olsun. Yemekleri TableViewController yapısıyla listelemek istediğimizde ListViewController içerisinde UITableViewDelegate ve UITableViewDataSource protocollerini çağırırız. Bu protocol ler numberOfRowsInSection ve cellForRowAt delegation metodlarını kullanmamızı dikte eder.

Bu şekilde ne yapmış oluruz?

ListViewController içerisinde yemek listesini bi yerlerden alırız ve ekranda gösteririz. Bunu yaparkende table hücrelerini yönetme işini UITableView protocol lerine delege ederiz.

 

Sahiplik

Tabii TableView protocollerinin kime hizmet edeceğini belirtmemiz gerekir. İşte bu yüzden yarattığımız TableView objesinin delegate ve datasource değişkenlerini viewDidLoad içinde “= self” deriz.

Yani bu durumu,

Bir şirkette birçok asistan ve birçok müdür olduğunu farz edersek bir müdürün gelip bir asistana “sen bundan sonra benim asistanımsın” demesine benzetebiliriz.

Yukarıdaki örnekte de TableView protocolleri ListViewController sınıfına asistanlık yapacağını belirtmek için ListViewController sınıfının viewDidLoad metodunda

yazarak sahipliği üzerine aldığını belirtmeliyiz.

 

Protocol Nedir?

Delegation konusunu açıklarken protocol sözünü de sık sık beraberinde telafuz ediyoruz. Peki nedir bu protocol? Ne işe yarar?

Protocolü OOP temellerinden bildiğimiz Interface gibi düşünebiliriz. Protocol de Interface gibi new diyerek yeni obje türetilemez. Protocolün barındırdığı delegation metodları vardır ve bu protocole uymak isteyen Controller sınıfı protokol e uymak istediğini belirttiğinde protocol ün ihtiva ettiği delegation metodlarını implement etmek zorunda kalır.

 

Protocol Nasıl Oluşturulur?

Şimdi bir örnek senaryo üzerinden protocol oluşturalım.

RegisterViewController sınıfının kullanıcıyı uygulamaya kayıt etmek için yazdığımız bir sınıf olsun ve bu kullanıcı kayıt sayfasına uygulamaya giriş yapmak için kullandığımız LoginViewController üzerinden erişsin.

Delegates and Protocols in Swift

Sayfa geçişlerin mutlaka bu şekilde olması gerekmiyor. Eğer dilerseniz sayfa geçişlerini segue kullanmadan veya her sayfa için farklı storyboard yaratarak da yapabilirsiniz. Konu ile alakalı olarak Farklı Storyboardlar ile Çalışmak yazıma göz atabilirsiniz. 

 

RegisterViewController kullanıcıyı kayıt ettikten sonra LoginViewControlller a kullanıcının bilgilerini haber vermesi gerekiyor.

İşte bu kısım delegation kullanmak için iyi bir fırsat. Aslında burada LoginViewController kullanıcıyı kayıt etme ve bundan haberdar etme işini RegisterViewController sınıfına delege ediyor diyebiliriz.

Bu nedenle RegisterViewController için protokol yazmalı ve LoginViewController ın bu protokolü implement etmesini sağlamalıyız.

Protokol için sınıf adı + “Delegate” isimlendirmesi uygun görülür. Yani RegisterViewController için oluşturacağımız protokol ismi RegisterViewControllerDelegate olmalıdır.

RegisterViewController sınıfının hemen üstüne şu şekilde bir protokol oluşturulabilir:

Fonksiyonların sadece signature larını tanımlıyoruz; implementasyonu olmayacak. Ayrıca sınıf içerisinde bu protokol tipinde Optional bir değişken oluşturmalıyız.

 

Haberleşme Nasıl Gerçekleşiyor?

İki sınıf arasındaki haberleşme şu şekilde gerçekleşecek:

Kullanıcı kayıt işlemi başarılı veya başarısız olduğu durumda ilgili protokol delegation metodunu delegate değişkeni üzerinden tetikleyeceğiz ve bu protokolü sınıfında implement etmiş olan LoginViewController sınıfı bundan tam o anda haberdar olacak.

Örnekte RegisterViewController sınıfı içinde register() isminde bir metodun olduğunu farz edelim. Metod ilgili web servisine istek atacak ve cevabını alacaktır. Bu noktada cevabın başarılı olduğu kısma gidip User objemizi doldurup delegation metodunu tetiklememiz gerekiyor.

Benzer şekilde isteğin başarısız olduğu durumda şunu yapmalıyız:

 

LoginViewController

RegisterViewController da yapmamız gerekenler bitti. Şimdi LoginViewController da yapmamız gereken 2 tane iş kaldı:

  • Delegation protokolünü implement etmek
  • Sahipliği üzerine almak

 

Delegation Protokolünü Implement Etmek

Şimdi LoginViewController sınıfına bir Extension yazalım ve sonra RegisterViewControllerDelegate protokolüne uymak istediğimizi belirtelim:

Bunu der demez şekildeki gibi bir hata almamız muhtemel:

Hatırlarsanız protokoller Interface gibidir demiştik. Interface içinde tanımlanan fonksiyonlar mutlaka çağrılmalıdır. Biz henüz çağırmadığımız için Xcode bizi uyarıyor. Hatta “istersen ben bu metodları senin için çağırayım” diyor. Fix butonuna bastığımızda delegation metodlarını implement ettiğini göreceksiniz.

Bu metodlar RegisterViewController tarafından tetiklendiğinde LoginViewController içinde de tetiklenmiş olacak.

Artık uygulamanızın business logic ine bağlı olarak bu metodları dilediğiniz gibi kullanabilirsiniz. Örneğin didUserRegisterFail metodu çalıştığında bir log tutabilir veya didUserRegisterSuccessfully metodu çalıştığında elinizdeki user objesini kullanarak kullanıcıyı otomatik olarak giriş yaptırabilirsiniz.

 

Sahipliği Üzerine Almak

Delegation sahipliğini üzerimize alırken o objeye sahip olduğumuzdan emin olmalıyız. Yani elimizde RegisterViewController dan türemiş bir objeye ihtiyacımız var ve RegisterViewController sayfasını açarken yaratmış olduğumuz o objeyi açtığımızdan emin olmalıyız.

Sayfa geçişleri için segue yapısını kullanıyorsanız şu yöntem izlenebilir:

performSegue(withIdentifier:sender:) metodu çağrılmadan hemen önce prepareforsegue fonksiyonu çağrılır ve pointer segue.destination objesini, yani birazdan açılacak olan sayfayı işaret eder. Dolayısıyla burda oluşturduğumuz RegisterViewController objesi ile açılacak olan RegisterViewController objesi aynı diyebiliriz. Bu yüzden delegation sahipliğini burada yapmak mantıklıdır.

 

Bonus ✨

Demin protokol içerisinde belirttiğimiz delegation metodlarının implement edilen sınıf tarafından mutlaka çağırılması gerektiğini söylemiştim. Aslında bu tam doğru değil çünkü protokoller opsiyonel metodlar barındırabiliyor ve protokolü implement eden sınıf bu opsiyonel metodları kullanmak zorunda kalmayabiliyor.

Hatırlarsanız TableView protokolünü kullanırken sadece numberOfRowsInSection ve cellForRowAt fonksiyonları mutlaka çağrılması gerekiyor ama TableView protokolünde bulunan fonksiyonlar sadece bunlar değil. Demekki;

  • willSelectRowAt
  • didSelectRowAt
  • heightForRowAt

gibi diğer TableView delegation metodları opsiyonel olarak işaretlenmiş.

Peki bunu kendi protokolümüz için nasıl yapacağız?

Opsiyonel olacak fonksiyon(lar) için protokole bir extension yazacağız ve implementasyonu olacakmış gibi süslü parentez açıp içini boş bırakacağız. Şöyleki:

 

Özet Olarak…

  1. Delegates and Protocols in Swift konusu sınıflar arası tek yönlü iletişim için mutlaka bilinmesi gereken bir yapıdır.
  2. Delegation bir veya birden fazla işi başkasına yaptırmaya denir.
  3. Protocol Interface gibi çalışır ve delegation metodlarını içerir.
  4. İletişimin gerçekleşmesi için ana sınıfın yardımcı sınıftan bir objesine ve delegate değişkenine sahip olması (self) gerekir.
  5. Yardımcı sınıf içinde delegation fonksiyonları doğru yerde tetiklenmeli ve bu fonksiyonların bulunduğu protokol ana sınıf içinde implement edilmelidir.
  6. Delegation fonksiyonları eğer istenirse opsiyonel yapılabilir.