Netwerken met NSURLSession deel 2

In de vorige zelfstudie heb ik je voorgesteld NSURLSession. Ik heb het gehad over de voordelen die het heeft NSURLConnection en hoe te gebruiken NSURLSession voor eenvoudige taken, zoals het ophalen van gegevens van een webservice en het downloaden van een afbeelding van internet. In deze zelfstudie bekijken we de configuratieopties van NSURLSession en hoe een downloadtaak te annuleren en te hervatten. We hebben veel terrein te dekken, dus laten we aan de slag gaan.


Sessieconfiguratie

Zoals we in de vorige tutorial zagen, een sessie, een instantie van NSURLSession, is een configureerbare container voor het indienen van netwerkverzoeken. De configuratie van de sessie wordt afgehandeld door een instantie van NSURLSessionConfiguration.

Een sessieconfiguratieobject is niets meer dan een woordenboek met eigenschappen dat definieert hoe de sessie waaraan het is gebonden zich gedraagt. Een sessie heeft één sessieconfiguratieobject dat cookie-, beveiligings- en cachebeleid dicteert, het maximale aantal verbindingen met een host, resource- en netwerktime-outs, enz. Dit is een aanzienlijke verbetering ten opzichte van NSURLConnection, die vertrouwden op een globaal configuratieobject met veel minder flexibiliteit.

veranderlijkheid

Zodra een sessie is gemaakt en geconfigureerd door een NSURLSessionConfiguration De configuratie van de sessie kan bijvoorbeeld niet worden gewijzigd. Als u de configuratie van een sessie moet wijzigen, moet u een nieuwe sessie maken. Houd er rekening mee dat het mogelijk is om de configuratie van een sessie te kopiëren en aan te passen, maar de wijzigingen hebben geen effect op de sessie waarvan de configuratie is gekopieerd.

Standaard configuratie

De NSURLSessionConfiguration class biedt drie fabrieksconstructeurs voor het instantiëren van standaardconfiguraties, defaultSessionConfiguration, ephemeralSessionConfiguration, en backgroundSessionConfiguration. De eerste methode retourneert een kopie van de standaard sessieconfiguratie object, wat resulteert in een sessie die zich op dezelfde manier gedraagt ​​als een NSURLConnection object in de standaardconfiguratie. Een sessieconfiguratie wijzigen die is verkregen via de defaultSessionConfiguration fabrieksmethode verandert de standaard sessieconfiguratie niet waarvan het een kopie is.

Kortstondige configuratie

Een sessieconfiguratie-object gemaakt door het aanroepen van de ephemeralSessionConfiguration fabrieksmethode zorgt ervoor dat de resulterende sessie geen permanente opslag gebruikt voor cookies, caches of inloggegevens. Met andere woorden, cookies, caches en inloggegevens worden bewaard in het geheugen. Kortstondige sessies zijn daarom ideaal als u privé browsen moet implementeren, iets dat eenvoudigweg niet mogelijk was vóór de introductie van NSURLSession.

Achtergrond configuratie

De backgroundSessionConfiguration: fabrieksmethode maakt een sessieconfiguratie-object dat uploads en downloads buiten het proces mogelijk maakt. De upload- en downloadtaken worden beheerd door een achtergronddaemon en blijven actief, zelfs als de toepassing is opgeschort of crasht. We zullen later in deze serie meer over achtergrondsessies praten.

Sessieconfiguratie

Zoals we in de vorige zelfstudie hebben gezien, is het maken van een sessieconfiguratieobject eenvoudig. In het onderstaande voorbeeld gebruikte ik de defaultSessionConfiguration fabrieksmethode om een ​​te maken NSURLSessionConfiguration aanleg. Het configureren van een sessieconfiguratieobject is net zo eenvoudig als het aanpassen van de eigenschappen ervan zoals in het voorbeeld. We kunnen dan het sessieconfiguratieobject gebruiken om een ​​sessieobject te instantiëren. Het sessieobject fungeert als een fabriek voor gegevens-, upload- en downloadtaken, waarbij elke taak overeenkomt met een enkel verzoek. In het onderstaande voorbeeld vragen we de iTunes Search API zoals we in de vorige zelfstudie hebben gedaan.

 // Maak sessieconfiguratie NSURLSessionConfiguration * sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; // Sessieconfiguratie configureren [sessionConfiguration setAllowsCellularAccess: YES]; [sessionConfiguration setHTTPAdditionalHeaders: @ @ "Accepteren": @ "application / json"]; // Maak sessie NSURLSession * sessie = [NSURLSession-sessie met configuratie: sessieconfiguratie]; // Verzend aanvraag NSURL * url = [NSURL URLWithString: @ "https://itunes.apple.com/search?term=apple&media=software"]; [[session dataTaskWithURL: url completionHandler: ^ (NSData * data, NSURLResponse * response, NSError * error) NSLog (@ "% @", [NSJSONSerialization JSONObjectWithData: data options: 0 error: nil]); ] hervatten];

Het voorbeeld illustreert ook hoe gemakkelijk het is om aangepaste headers toe te voegen door de HTTPAdditionalHeaders eigenschap van het sessieconfiguratieobject. De schoonheid van de NSURLSession API is dat elk verzoek dat de sessie passeert, wordt geconfigureerd door het configuratieobject van de sessie. Het toevoegen van verificatiekoppen aan een reeks verzoeken, bijvoorbeeld, wordt eenvoudig als taart.


Annuleren en hervatten van downloads

In de vorige zelfstudie heb ik je laten zien hoe je een afbeelding kunt downloaden met behulp van de NSURLSession API. Netwerkverbindingen zijn echter onbetrouwbaar en het gebeurt maar al te vaak dat een download mislukt vanwege een slechte netwerkverbinding. Gelukkig is het hervatten van een download niet moeilijk met de NSURLSession API. In het volgende voorbeeld laat ik u zien hoe u het downloaden van een afbeelding kunt annuleren en hervatten.

Voordat we de downloadtaak verder bekijken, is het belangrijk om het verschil te begrijpen tussen het annuleren en opschorten van een downloadtaak. Het is mogelijk om een ​​downloadtaak te onderbreken en op een later tijdstip te hervatten. Het annuleren van een downloadtaak stopt echter de taak en het is niet mogelijk om deze op een later tijdstip te hervatten. Er is echter een alternatief. Het is mogelijk om een ​​downloadtaak te annuleren door te bellen cancelByProducingResumeData: ben ermee bezig. Het accepteert een voltooiingshandler die één parameter accepteert, een NSData object dat wordt gebruikt om het downloaden op een later tijdstip te hervatten door op te roepen downloadTaskWithResumeData: of downloadTaskWithResumeData: completionHandler: op een sessieobject. De NSData object bevat de nodige informatie om de downloadtaak te hervatten waar hij gebleven was.

Stap 1: Outlets en acties

Open het project dat we in de vorige tutorial hebben gemaakt of download het hier. We beginnen met het toevoegen van twee knoppen aan de gebruikersinterface, een om de download te annuleren en een om de download te hervatten. Maak in het header-bestand van de view-controller een uitloop en een actie voor elke knop, zoals hieronder wordt getoond.

 #importeren  @interface MTViewController: UIViewController @property (weak, nonatomic) IBOutlet UIButton * cancelButton; @property (weak, nonatomic) IBOutlet UIButton * resumeButton; @property (weak, nonatomic) IBOutlet UIImageView * imageView; @property (weak, nonatomic) IBOutlet UIProgressView * progressView; - (IBAction) cancel: (id) afzender; - (IBAction) cv: (id) afzender; @einde

Stap 2: Gebruikersinterface

Open het belangrijkste storyboard van het project en voeg twee knoppen toe aan de weergave van de view controller. Plaats de knoppen zoals weergegeven in de onderstaande schermafbeelding en verbind elke knop met het bijbehorende stopcontact en de juiste actie.


Stap 3: Refactoring

We moeten wat refactoring uitvoeren om alles correct te laten werken. Open MTViewController.m en een instantie-variabele en twee eigenschappen declareren. De instantievariabele, sessie, bewaart een verwijzing naar de sessie die we zullen gebruiken voor het downloaden van de afbeelding.

 #import "MTViewController.h" @interface MTViewController ()  NSURLSession * _session;  @property (strong, nonatomic) NSURLSessionDownloadTask * downloadTask; @property (strong, nonatomic) NSData * resumeData; @einde

We moeten ook de refactor aanpassen viewDidLoad methode, maar eerst wil ik een gettermethode voor de sessie implementeren. De implementatie is vrij eenvoudig, zoals hieronder te zien is. We maken een sessieconfiguratie-object met behulp van de defaultSessionConfiguration fabrieksmethode en instantiëren het sessieobject ermee. De view controller dient als de gedelegeerde van de sessie.

 - (NSURLSession *) sessie if (! _Session) // Create Session Configuration NSURLSessionConfiguration * sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; // Create Session _session = [NSURLSession sessionWithConfiguration: sessionConfiguration delegate: self delegateQueue: nil];  return _session; 

Met de sessie accessor geïmplementeerd, de viewDidLoad methode wordt veel eenvoudiger. We maken een downloadtaak, zoals we in de vorige zelfstudie hebben gedaan, en slaan een verwijzing naar de taak op downloadTask. Vervolgens vertellen we de downloadtaak aan hervatten.

 - (void) viewDidLoad [super viewDidLoad]; // Maak downloadtaak aan self.downloadTask = [self.session downloadTaskWithURL: [NSURL URLWithString: @ "http://cdn.tutsplus.com/mobile/uploads/2014/01/5a3f1-sample.jpg"]]; // Doorgaan met downloaden taak [self.downloadTask CV]; 

Stap 4: De download annuleren

De annuleren: actie bevat de logica voor het annuleren van de downloadtaak die we zojuist hebben gemaakt. Als downloadTask is niet nul, wij bellen cancelByProducingResumeData: op de taak. Deze methode accepteert één parameter, een voltooiingsblok. Het voltooiingsblok neemt ook één parameter, een instantie van NSData. Als resumeData is niet nul, we slaan een verwijzing naar het data-object op in viewcontrollers resumeData eigendom.

Als een download niet kan worden hervat, is het voltooiingsblok resumeData parameter is nul. Niet elke download is hervat, dus het is belangrijk om te controleren of resumeData is geldig NSData voorwerp.

 - (IBAction) cancel: (id) afzender if (! Self.downloadTask) retour; // Hide Cancel-knop [self.cancelButton setHidden: YES]; [self.downloadTask cancelByProducingResumeData: ^ (NSData * resumeData) if (! resumeData) return; [self setResumeData: resumeData]; [self setDownloadTask: nil]; ]; 

Stap 5: De download hervatten

Het hervatten van de downloadtaak nadat deze was geannuleerd, is eenvoudig. In de hervatten: actie, we controleren of de view controller's resumeData eigendom is ingesteld. Als resumeData is geldig NSData object, we vertellen het sessie object om een ​​nieuwe downloadtaak aan te maken en deze door te geven NSData voorwerp. Dit is alles sessie object moet de downloadtaak opnieuw creëren die we hebben geannuleerd in de annuleren: actie. Vervolgens vertellen we de downloadtaak aan hervatten En instellen resumeData naar nul.

 - (IBAction) cv: (id) afzender if (! Self.resumeData) retour; // Hide Resume Button [self.resumeButton setHidden: YES]; // Maak downloadtaak aan self.downloadTask = [self.session downloadTaskWithResumeData: self.resumeData]; // Doorgaan met downloaden taak [self.downloadTask CV]; // Cleanup [self setResumeData: nil]; 

Bouw het project en voer de applicatie uit in de iOS Simulator of op een fysiek apparaat. De download zou automatisch moeten starten. Tik op de knop Annuleren om het downloaden te annuleren en tik op de knop Doorgaan om het downloaden te hervatten.

Stap 6: Afwerking

Er zijn een aantal details waarvoor we moeten zorgen. Allereerst moeten de knoppen niet altijd zichtbaar zijn. We zullen waarnemen van sleutelwaarden gebruiken om de knoppen te tonen en te verbergen wanneer nodig. In viewDidLoad, verberg de knoppen en voeg de view controller toe als waarnemer van zichzelf voor de resumeData en downloadTask sleutelpaden.

 - (void) viewDidLoad [super viewDidLoad]; // Observer toevoegen [self addObserver: self forKeyPath: @ "resumeData" -opties: NSKeyValueObservingOptionNew context: NULL]; [self addObserver: self forKeyPath: @ "downloadTask" opties: NSKeyValueObservingOptionNew context: NULL]; // Gebruikersinterface instellen [self.cancelButton setHidden: YES]; [self.resumeButton setHidden: YES]; // Maak downloadtaak aan self.downloadTask = [self.session downloadTaskWithURL: [NSURL URLWithString: @ "http://cdn.tutsplus.com/mobile/uploads/2014/01/5a3f1-sample.jpg"]]; // Doorgaan met downloaden taak [self.downloadTask CV]; 

In observeValueForKeyPath: ofObject: change: context:, we verbergen de annuleerknop als resumeData is nul en we verbergen de hervattingsknop als downloadTask is nul. Bouw het project en voer de applicatie nog een keer uit om het resultaat te zien. Dit is beter. Rechts?

 - (void) observeValueForKeyPath: (NSString *) keyPath ofObject: (id) objectwijziging: (NSDictionary *) verander context: (void *) context if ([keyPath isEqualToString: @ "resumeData"]) dispatch_async (dispatch_get_main_queue (), ^ [self.resumeButton setHidden: (self.resumeData == nil)];);  else if ([keyPath isEqualToString: @ "downloadTask"]) dispatch_async (dispatch_get_main_queue (), ^ [self.cancelButton setHidden: (self.downloadTask == nil)];); 
Zoals George Yang in de opmerkingen aangeeft, weten we niet of observeValueForKeyPath: ofObject: change: context: wordt aangeroepen op de hoofdthread. Het is daarom belangrijk om de gebruikersinterface bij te werken in een GCD-blok (Grand Central Dispatch) dat wordt aangeroepen in de hoofdwachtrij.

Stap 7: De sessie ongeldig maken

Er is een belangrijk aspect van NSURLSession waar ik nog niet over gesproken heb, sessie-ongeldigverklaring. De sessie houdt een sterke verwijzing naar zijn afgevaardigde bij, wat betekent dat de afgevaardigde niet wordt vrijgegeven zolang de sessie actief is. Om deze referentiecyclus te doorbreken, moet de sessie ongeldig worden verklaard. Wanneer een sessie ongeldig wordt gemaakt, worden actieve taken geannuleerd of beëindigd en wordt de deelnemer verzonden URLSession: didBecomeInvalidWithError: bericht en de sessie geeft zijn gemachtigde vrij.

Er zijn verschillende plaatsen waar we de sessie kunnen ongeldig maken. Omdat de view-controller slechts één afbeelding downloadt, kan de sessie ongeldig worden gemaakt als het downloaden is voltooid. Bekijk de bijgewerkte implementatie van URLSession: downloadTask: didFinishDownloadingToURL:. De knop Annuleren is ook verborgen wanneer het downloaden is voltooid.

 - (void) URLSession: (NSURLSession *) sessie downloadTask: (NSURLSessionDownloadTask *) downloadTask didFinishDownloadingToURL: (NSURL *) locatie NSData * data = [NSData dataWithContentsOfURL: location]; dispatch_async (dispatch_get_main_queue (), ^ [self.cancelButton setHidden: YES]; [self.progressView sethidden: YES]; [self.imageView setImage: [UIImage imageWithData: data]];); // Invalidate Session [session finishTasksAndInvalidate]; 

Conclusie

Het voorbeeldproject dat we in deze zelfstudie hebben gemaakt, is een vereenvoudigde implementatie van het annuleren en hervatten van downloads. In uw toepassingen kan het nodig zijn om de. Te schrijven resumeData object naar schijf voor later gebruik en het is mogelijk dat meerdere downloadtaken tegelijkertijd worden uitgevoerd. Hoewel dit de complexiteit verhoogt, blijven de basisprincipes hetzelfde. Zorg dat u geheugenlekken voorkomt door altijd een sessie ongeldig te maken die u niet meer nodig hebt.