Delegation Pattern – SWIFT

Delegation pattern reprezintă un mijloc elegant prin care se realiza comunicarea între instanțe, o situație foarte des întâlnită fiind aceea a transferului de informații între diferite viewcontroller-e. Pentru cei din Apple@DMT, situația este des întâlnită.

Pentru început să considerăm clasele următoare:

class LoginAPIService {
}
class LoginViewController {
  private let callLoginService = LoginAPIService()
}

În acest moment avem două clase LoginAPIService – ce pune la dispoziție servicii specifice de login și LoginViewController – clasă care va acoperi un View Controller desenat în Storyboard, de exemplu.

Putem transmite mesaje către LoginAPIService foarte lejer prin instanța privată callLoginService. E o problemă, în schimb, transmiterea de informații de la LoginAPIService la  LoginViewController.

Cineva ar putea spune: de ce să nu facem la fel, o instanță privată LoginViewController  în LoginAPIService? Păi dacă LoginAPIService este un serviciu oferit ViewController-elor, e nevoie de câte o instanță a fiecărui ViewController în LoginAPIService. E puțin peste mână o astfel de abordare și, prin urmare, trebuie să găsim o soluție pentru această problemă a transmisiei de mesaje de la LoginAPIService către LoginViewController.

Delegation pattern reprezintă  soluția pentru astfel de probleme.  Apple ne ajută să implementăm astfel de pattern-uri prin intermediul protocoalelor.

Protocolul, pe scurt, reprezintă o clasă abstractă care, atunci când cineva aderă la el, trebuie obligatoriu implementat. Altfel spus, daca ader la un protocol, trebuie sa implementez toate metodele declarate de către acesta.

protocol LoginAPIDelegate: class {
  func didRetrieveLoginUserDetailsFromServer(_ user: UserModel)
}

Cum folosim acest protocol în continuare?

class LoginAPIService {
weak var delegate: LoginAPIDelegate?
} 

Ce este acest delegate  declarat ca și  proprietate în interiorul LoginAPIService?

Este o instanță de LoginAPIService care mă asigură de faptul că toate metodele definite în interiorul protocolului vor fi implementate de către această proprietate.

Cine anume “va dori” să fie de fapt acest delegat? Dacă vom extinde clasa LoginViewController astfel încât ea să fie conformă cu protocolul nostru LoginAPIDelegate, atunci acea proprietate delegate va putea fi chiar LoginViewController. 

extension LoginViewController: LoginAPIDelegate {
    func didRetrieveLoginUserDetailsFromServer(_ user: UserModel)
 {
    // prelucram datele care au fost furnizate de catre serviciu 
  }
}

Ceea ce este cel mai interesant, acum urmează. În interiorul clasei LoginViewController specificăm că aceasta este, de fapt cea care-și atribuie calitatea de delegat pentru instanța callLoginService de tip LoginAPIService. Astfel, callLoginService va putea comunica mesaje de la LoginViewController -> LoginAPIService, iar callLoginService.delegate va comunica mesaje de la LoginAPIService -> LoginViewController. 

Comunicare  LoginAPIService -> LoginViewController 

Pentru a realiza transportul de mesaje de la  LoginAPIService către LoginViewController vom face apel de  didRetrieveLoginUserDetailsFromServer(_: ). Astfel, cine și-a asumat rolul de delegat (deci e conform cu protocolul LoginAPIDelegate), va executa apelul. 

class LoginAPIService {
  weak var delegate: LoginAPIDelegate?
  func connectToServer() {
    // facem request-ul de login și ambalăm datele în userModel 
    delegate?.didRetrieveLoginUserDetailsFromServer(userModel)
  }
}

Astfel, am reușit să rezolvăm problema transferului de mesaje între cele două entități.

Share Button

De ce este necesară delegarea unui UIScrollView?

UIScrollView este un view în care vom putea efectua operații de zooming. Pentru a realiza acest lucru avem câțiva pași de urmat pentru a “pregăti” scrollview-ul.
1. Adăugăm în Storyboard un controller ScrollView și-l legăm de un outlet, să zicem scrollView in .h aferent
@property (strong, nonatomic) IBOutlet UIScrollView *scrollView;
2. Definim un ImageView in .h care va încărca imaginea în care vom face zoom sau pinch.
@property (strong, nonatomic) UIImageView *imageView;
3. Ambele proprietăți vor fi sintetizate pentru a putea fi utilizate în .m
@synthesize imageView=_imageView;
@synthesize scrollView=_scrollView;
4. În viewDidLoad: încărcăm poza pe care ne vom “juca”
self.imageView=[[UIImageView alloc] initWithImage:[UIImage imageNamed:@”photo5.png”]];
5. Dimensionăm scrollView-ul cu dimensiunea imaginii încărcate
self.scrollView.contentSize=self.imageView.frame.size;
6. Legăm imageView-ul de scrollView ca un subview în stivă.
[self.scrollView addSubview:self.imageView];

În acest moment avem un scrollView în care am încărcat un imageView funcțional.

UIScrollView, fiind un view, nu știe să lucreze cu evenimentele apărute asupra sa. Prin urmare trebuie să delege pe cineva care să interpreteze acțiunile. Acest cineva este protocolul UIScrollViewDelegate. Pentru a-l pune la treabă, mai întâi îl adaugăm în .h
@interface ViewController : UIViewController
și urmărim care sunt metodele lui UIScrollViewDelegate ce trebuie musai implementate.
1. Cine anume se va scala atunci când se va face zooming? O întrebare legitimă. imageView-ul va suferi zooming-ul atunci cand el va aparea in scroolView.
– (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.imageView;
}

2. Delegăm pe self, adică ViewController-ul principal ca fiind delegat pentru scrollView. Acest ViewController implementează protocolul UIScrollViewDelegate, astfel încât scrollView-ul să “beneficieze” de metodele protocolului
self.scrollView.delegate=self;
3. Specificăm valorile maximale/minimale pentru zooming
self.scrollView.maximumZoomScale=5.0;
self.scrollView.minimumZoomScale=0.5;

Având toate acestea implementate, scrollView-ul va funcționa.

Share Button