Lucrul în rețea cu NSURLSession Partea 2

În tutorialul anterior, te-am prezentat NSURLSession. Am vorbit despre avantajele pe care le-a avut NSURLConnection și cum se utilizează NSURLSession pentru sarcini simple, cum ar fi preluarea datelor de la un serviciu web și descărcarea unei imagini de pe web. În acest tutorial, vom examina mai îndeaproape opțiunile de configurare din NSURLSession și cum să anulați și să reluați o sarcină de descărcare. Avem o mulțime de teren pentru a acoperi așa că să începem.


Sesiunea de configurare

După cum am văzut în tutorialul anterior, o sesiune, o instanță de NSURLSession, este un container configurabil pentru introducerea cererilor de rețea. Configurația sesiunii este tratată de o instanță de NSURLSessionConfiguration.

Un obiect de configurare a sesiunii nu este altceva decât un dicționar al proprietăților care definește cum se comportă sesiunea pe care este legată. O sesiune are un obiect de configurare a sesiunii care dictează politicile cookie-ului, securității și cache-ului, numărul maxim de conexiuni la o gazdă, timeout-urile resurselor și rețelei etc. Aceasta reprezintă o îmbunătățire semnificativă față de NSURLConnection, care sa bazat pe un obiect de configurație global cu mult mai puțină flexibilitate.

Mutabilitate

Odată ce o sesiune este creată și configurată de a NSURLSessionConfiguration exemplu, configurația sesiunii nu poate fi modificată. Dacă trebuie să modificați configurația unei sesiuni, trebuie să creați o nouă sesiune. Rețineți că este posibil să copiați configurația unei sesiuni și să o modificați, dar modificările nu au niciun efect asupra sesiunii din care a fost copiată configurația.

Configurația implicită

NSURLSessionConfiguration clasa oferă trei constructori din fabrică pentru configurarea configurațiilor standard, defaultSessionConfiguration, ephemeralSessionConfiguration, și backgroundSessionConfiguration. Prima metodă returnează o copie a textului setarea implicită a sesiunii obiect, care are ca rezultat o sesiune care se comportă similar cu o sesiune NSURLConnection obiect în configurația sa standard. Modificarea unei configurații de sesiune obținută prin defaultSessionConfiguration metoda de fabrică nu modifică configurația sesiunii implicite pe care o copiază.

Configurația efemerică

Un obiect de configurare a sesiunii creat prin invocarea ephemeralSessionConfiguration metoda de fabrică asigură că sesiunea rezultată nu utilizează stocări permanente pentru cookie-uri, cache-uri sau acreditări. Cu alte cuvinte, cookie-urile, cache-urile și acreditările sunt păstrate în memorie. Sesiunile efemere sunt, prin urmare, ideale dacă aveți nevoie pentru a implementa navigarea privată, ceva care pur și simplu nu a fost posibil înainte de introducerea NSURLSession.

Configurația de fundal

backgroundSessionConfiguration: metoda fabrică creează un obiect de configurare a sesiunii care permite încărcările și descărcările în afara procesului. Sarcinile de încărcare și descărcare sunt gestionate de un daemon de fundal și continuă să ruleze chiar dacă aplicația este suspendată sau blocată. Vom vorbi mai târziu despre sesiunile de fundal din această serie.

Sesiunea de configurare

După cum am văzut în tutorialul anterior, crearea unui obiect de configurare a sesiunii este simplă. În exemplul prezentat mai jos, am folosit funcția defaultSessionConfiguration metoda fabricii pentru a crea o NSURLSessionConfiguration instanță. Configurarea unui obiect de configurare a sesiunii este la fel de simplă ca modificarea proprietăților sale, așa cum se arată în exemplu. Apoi putem folosi obiectul de configurare a sesiunii pentru a instantiza un obiect de sesiune. Obiectul de sesiune funcționează ca o fabrică pentru sarcini de date, încărcare și descărcare, fiecare activitate corespund unei singure solicitări. În exemplul de mai jos, interogăm API-ul de căutare iTunes ca în tutorialul precedent.

 // Crearea configurării sesiunii NSURLSessionConfiguration * sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; // Configurați configurarea sesiunii [sessionConfiguration setAllowsCellularAccess: YES]; [sessionConfiguration setHTTPAdditionalHeaders: @ @ "Accept": @ "application / json"]; // Creați sesiune NSURLSession * session = [NSURLSession sessionWithConfiguration: sessionConfiguration]; // Trimite cererea NSURL * url = [NSURL URLWithString: @ "https://itunes.apple.com/search?term=apple&media=software"]; [[data sesiuneTaskWithURL: url completionHandler: ^ (NSData * date, răspuns NSURLResponse *, eroare NSError *) NSLog (@ "% @", [NSJSONSerializare JSONObjectWithData: opțiuni de date: 0 eroare: zero]); ] relua];

Exemplul ilustrează, de asemenea, cât de ușor este să adăugați anteturi personalizate prin setarea HTTPAdditionalHeaders proprietatea obiectului de configurare a sesiunii. Frumusetea NSURLSession API este că fiecare solicitare care trece prin sesiune este configurată de obiectul de configurare a sesiunii. Adăugarea antetelor de autentificare la un set de solicitări, de exemplu, devine ușor ca plăcintă.


Anularea și reluarea descărcărilor

În tutorialul anterior, v-am arătat cum să descărcați o imagine utilizând NSURLSession API-ul. Cu toate acestea, conexiunile de rețea nu sunt fiabile și se întâmplă prea des ca descărcarea să se deterioreze datorită unei conexiuni slabe la rețea. Din fericire, reluarea descărcării nu este dificilă cu NSURLSession API-ul. În următorul exemplu, vă voi arăta cum să anulați și să reluați descărcarea unei imagini.

Înainte de a examina mai atent reluarea unei sarcini de descărcare, este important să înțelegeți diferența dintre anularea și suspendarea unei sarcini de descărcare. Este posibil să suspendați o sarcină de descărcare și să o reluați mai târziu. Anularea unei sarcini de descărcare, însă, oprește sarcina și nu este posibilă reluarea acesteia mai târziu. Există însă o alternativă. Este posibil să anulați o sarcină de descărcare apelând cancelByProducingResumeData: pe el. Acceptă un handler de completare care acceptă un parametru, un NSData obiect care este folosit pentru a relua descărcarea ulterioară prin invocarea downloadTaskWithResumeData: sau downloadTaskWithResumeData: completionHandler: pe un obiect de sesiune. NSData obiect conține informațiile necesare pentru a relua sarcina de descărcare unde a rămas.

Pasul 1: Puncte de vânzare și acțiuni

Deschideți proiectul pe care l-am creat în tutorialul precedent sau îl descărcați aici. Începem prin adăugarea a două butoane la interfața cu utilizatorul, una pentru a anula descărcarea și una pentru a relua descărcarea. În fișierul header al controlerului de vizualizare, creați o priză și o acțiune pentru fiecare buton, după cum se arată mai jos.

 #import  @interface MTViewController: UIViewController @property (slab, nonatomic) IBOutlet UIButton * cancelButton; @property (slab, nonatomic) IBOutlet UIButton * resumeButton; @property (slab, nonatomic) IBOutlet UIImageView * imageView; @property (slab, nonatomic) IBOutlet UIProgressView * progressView; - (IBAction) anulați: (id) expeditor; - (IBAction) CV: (id) expeditor; @Sfârșit

Pasul 2: Interfața utilizatorului

Deschideți tabloul de bord principal al proiectului și adăugați două butoane la vizualizarea controlerului de vizualizare. Poziționați butoanele așa cum se arată în imaginea de mai jos și conectați fiecare buton cu priza și acțiunea corespunzătoare.


Pasul 3: Refactorizarea

Va trebui să facem niște refactorizări pentru ca totul să funcționeze corect. Deschis MTViewController.m și să declare o variabilă de instanță și două proprietăți. Variabila instanței, sesiune, va păstra o referință la sesiunea pe care o vom folosi pentru descărcarea imaginii.

 #import "MTViewController.h" @interface MTViewController ()  NSURLSession * _session;  @property (puternic, nonatomic) NSURLSessionDownloadTask * downloadTask; @property (puternic, nonatomic) NSData * resumeData; @Sfârșit

Trebuie, de asemenea, să refactorizăm viewDidLoad , dar mai întâi aș vrea să pun în aplicare o metodă getter pentru sesiune. Implementarea sa este destul de simplă, după cum puteți vedea mai jos. Creăm un obiect de configurare a sesiunii folosind defaultSessionConfiguration fabrică și instanțiați obiectul sesiunii cu acesta. Controlerul de vizualizare servește drept delegat al sesiunii.

 - (NSURLSession *) sesiune if (! _Session) // Configurarea sesiunii de configurare NSURLSessionConfiguration * sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; // Creați sesiunea _session = [NSURLSession sessionWithConfiguration: delegația sesiuniiConfiguration: self delegateQueue: nil];  retur _session; 

Cu sesiune accesoriu implementat, viewDidLoad metoda devine mult mai simplă. Creați o sarcină de descărcare, așa cum am făcut în tutorialul anterior, și stocați o referință la sarcina din downloadTask. Apoi spunem sarcina de descărcare relua.

 - (vid) viewDidLoad [super viewDidLoad]; // Creați sarcina de descărcare self.downloadTask = [auto.session downloadTaskWithURL: [NSURL URLWithString: @ "http://cdn.tutsplus.com/mobile/uploads/2014/01/5a3f1-sample.jpg"]]; // Reluați sarcina de descărcare [self.downloadTask resume]; 

Pasul 4: Anularea descărcării

Anulare: acțiune conține logica pentru anularea sarcinii de descărcare pe care tocmai am creat-o. Dacă downloadTask nu este zero, noi sunam cancelByProducingResumeData: cu privire la sarcină. Această metodă acceptă un parametru, un bloc de completare. Blocul de completare are de asemenea un parametru, un exemplu de NSData. Dacă resumeData nu este zero, stocăm o referință la obiectul de date în vizualizarea controlerului resumeData proprietate.

Dacă descărcarea nu poate fi reluată, blocul de completare resumeData este parametrul zero. Nu toate descărcările sunt reluabile, deci este important să verificați dacă resumeData este valabil NSData obiect.

 - (IBAction) anulați: (id) expeditor if (! Self.downloadTask) retur; // Ascundeți butonul Anulare [auto.cancelButton setHidden: YES]; [auto.downloadTask cancelByProducingResumeData: ^ (NSData * resumeData) dacă (! resumeData) retur; [auto setResumeData: resumeData]; [auto setDownloadTask: zero]; ]; 

Pasul 5: Reluarea descărcării

Reluarea sarcinii de descărcare după ce a fost anulată este ușoară. În relua: acțiune, verificăm dacă controlerul de vizualizare resumeData proprietatea este setată. Dacă resumeData este valabil NSData obiect, spunem sesiune obiect de a crea o nouă sarcină de descărcare și de ao transmite NSData obiect. Toate acestea sunt sesiune obiect trebuie să recreeze sarcina de descărcare pe care am anulat-o în Anulare: acțiune. Apoi spunem sarcina de descărcare relua și stabilit resumeData la zero.

 - (IBAction) relua: (id) expeditorul if (! Self.resumeData) retur; // Ascundeți butonul Resume [auto.resumeButton setHidden: YES]; // Creați sarcina de descărcare self.downloadTask = [auto.session downloadTaskWithResumeData: self.resumeData]; // Reluați sarcina de descărcare [self.downloadTask resume]; // Curățire [auto setResumeData: zero]; 

Construiți proiectul și rulați aplicația în Simulatorul iOS sau pe un dispozitiv fizic. Descărcarea ar trebui să înceapă automat. Atingeți butonul anulare pentru a anula descărcarea și atingeți butonul CV pentru a relua descărcarea.

Pasul 6: Sfaturi de finisare

Există o serie de detalii pe care trebuie să le avem în vedere. În primul rând, butoanele nu ar trebui să fie întotdeauna vizibile. Vom folosi observarea valorilor cheie pentru a afișa și a ascunde butoanele atunci când este necesar. În viewDidLoad, ascundeți butoanele și adăugați controlerul de vizualizare ca observator al acesteia pentru resumeData și downloadTask căi cheie.

 - (vid) viewDidLoad [super viewDidLoad]; // Add Observer [self addObserver: self forKeyPath: @ "resumeData" opțiuni: NSKeyValueObservingOptionNew context: NULL]; [self addObserver: auto pentruKeyPath: @ "opțiunea" downloadTask ": NSKeyValueObservingOptionNew context: NULL]; // Configurare interfață utilizator [self.cancelButton setHidden: YES]; [self.resumeButton setHidden: YES]; // Creați sarcina de descărcare self.downloadTask = [auto.session downloadTaskWithURL: [NSURL URLWithString: @ "http://cdn.tutsplus.com/mobile/uploads/2014/01/5a3f1-sample.jpg"]]; // Reluați sarcina de descărcare [self.downloadTask resume]; 

În observeValueForKeyPath: ofObject: schimbare: Context:, ascundem butonul de anulare dacă resumeData este zero și ascundem butonul de reluare dacă downloadTask este zero. Construiți proiectul și rulați aplicația încă o dată pentru a vedea rezultatul. Asa este mai bine. Dreapta?

 - (void) observeValueForKeyPath: (NSString *) cheiePath ofObject: (id) schimbare obiect: (NSDictionary *) context de schimbare: (void *) context if ([keyPath esteEqualToString: @ "resumeData"]) dispatch_async (dispatch_get_main_queue ^ [auto.resumeButton setHidden: (auto.resumeData == nil)];);  altfel dacă ([keyPath esteEqualToString: @ "downloadTask"]) dispatch_async (dispatch_get_main_queue (), ^ [auto.cancelButton setHidden: (auto.downloadTask == nil)];); 
După cum subliniază George Yang în comentarii, nu știm dacă observeValueForKeyPath: ofObject: schimbare: Context: este chemat pe firul principal. Prin urmare, este important să actualizați interfața de utilizator într-un bloc GCD (Grand Central Dispatch) care este invocat pe coada principală.

Pasul 7: Invalidarea sesiunii

Există un aspect cheie al NSURLSession despre care nu am vorbit încă, invalidarea sesiunii. Sesiunea păstrează o referință puternică la delegatul său, ceea ce înseamnă că delegatul nu este eliberat atâta timp cât sesiunea este activă. Pentru a întrerupe acest ciclu de referință, sesiunea trebuie invalidată. Atunci când o sesiune este invalidată, sarcinile active sunt anulate sau terminate, iar delegatul este trimis a URLSession: didBecomeInvalidWithError: mesaj și sesiunea îi eliberează pe delegat.

Există mai multe locuri pe care le putem invalida sesiunea. Deoarece controlerul de vizualizare descarcă doar o singură imagine, sesiunea poate fi invalidată la finalizarea descărcării. Uitați-vă la implementarea actualizată a URLSession: downloadTask: didFinishDownloadingToURL:. Butonul de anulare este, de asemenea, ascuns când se termină descărcarea.

 - (void) URLSession: (NSURLSession *) sesiune downloadTask: (NSURLSessionDownloadTask *) downloadTask didFinishDownloadingToURL: (NSURL *) locație NSData * data = [NSData dataWithContentsOfURL: locație]; dispatch_async (dispatch_get_main_queue (), auto.cancelButton setHidden: YES]; [self.progressView setHidden: YES]; [auto.imageView setImage: [UIImage imageWithData: data]];)); // Invalidați sesiunea [session endTasksAndInvalidate]; 

Concluzie

Proiectul de exemplu pe care l-am creat în acest tutorial este o implementare simplificată a modului de anulare și reluare a descărcărilor. În aplicațiile dvs., poate fi necesar să scrieți resumeData obiect la disc pentru utilizare ulterioară și este posibil ca mai multe sarcini de descărcare să fie difuzate în același timp. Chiar dacă aceasta adaugă complexitate, principiile de bază rămân aceleași. Asigurați-vă că preveniți scurgeri de memorie prin invalidarea întotdeauna a unei sesiuni pe care nu mai aveți nevoie.

Cod