Nettverk med NSURLSession Del 2

I den forrige opplæringen introduserte jeg deg til NSURLSession. Jeg snakket om fordelene den har over NSURLConnection og hvordan du bruker det NSURLSession for enkle oppgaver, for eksempel å hente data fra en webtjeneste og laste ned et bilde fra nettet. I denne opplæringen tar vi nærmere titt på konfigurasjonsalternativene for NSURLSession og hvordan du avbryter og gjenopptar en nedlastingsoppgave. Vi har mye grunn til å dekke så la oss komme i gang.


Session Configuration

Som vi så i forrige veiledning, en økt, en forekomst av NSURLSession, er en konfigurerbar beholder for å sette nettverksforespørsler inn i. Konfigurasjonen av økten håndteres av en forekomst av NSURLSessionConfiguration.

Et øktkonfigurasjonsobjekt er ingenting mer enn en ordbok med egenskaper som definerer hvordan økten den er bundet til å oppføre seg. En økt har ett sesjonskonfigurasjonsobjekt som dikterer informasjon om informasjonskapsler, sikkerhet og buffer, maksimalt antall tilkoblinger til vert, ressurs- og nettverksavbrudd etc. Dette er en betydelig forbedring i forhold til NSURLConnection, som stod på et globalt konfigurasjonsobjekt med mye mindre fleksibilitet.

mutability

Når en økt er opprettet og konfigurert av en NSURLSessionConfiguration For eksempel kan sesjonens konfigurasjon ikke endres. Hvis du må endre en økts konfigurasjon, må du opprette en ny økt. Husk at det er mulig å kopiere en økts konfigurasjon og endre den, men endringene har ingen effekt på økten der konfigurasjonen ble kopiert.

Standard konfigurasjon

De NSURLSessionConfiguration klassen gir tre fabrikkkonstruksjoner for å instansere standardkonfigurasjoner, defaultSessionConfiguration, ephemeralSessionConfiguration, og backgroundSessionConfiguration. Den første metoden returnerer en kopi av standard økt konfigurasjon objekt, noe som resulterer i en økt som oppfører seg på samme måte som en NSURLConnection objekt i standardkonfigurasjonen. Endre en øktkonfigurasjon oppnådd gjennom defaultSessionConfiguration Fabrikkmetoden endrer ikke standard sesjonskonfigurasjon som det er en kopi av.

Ephemeral Configuration

Et økt konfigurasjonsobjekt opprettet ved å påkalle ephemeralSessionConfiguration Fabrikkmetoden sikrer at den resulterende økten ikke bruker vedvarende lagring for informasjonskapsler, cacher eller legitimasjonsbeskrivelser. Med andre ord, er cookies, cacher og legitimasjon lagret i minnet. Økologiske økter er derfor ideelle hvis du trenger å implementere privat surfing, noe som ikke var mulig før innføringen av NSURLSession.

Bakgrunnskonfigurasjon

De backgroundSessionConfiguration: fabrikkmetoden oppretter et øktkonfigurasjonsobjekt som gjør det mulig å laste opp og laste ned utenom prosessen. Opplastings- og nedlastingsoppgaver styres av en bakgrunnsdemon og fortsetter å kjøre selv om programmet er suspendert eller krasjer. Vi snakker mer om bakgrunnsøkter senere i denne serien.

Session Configuration

Som vi så i den forrige opplæringen, er det enkelt å lage et øktkonfigurasjonsobjekt. I eksemplet som vist nedenfor brukte jeg defaultSessionConfiguration fabrikk metode for å lage en NSURLSessionConfiguration forekomst. Konfigurere et øktkonfigurasjonsobjekt er like enkelt som å endre egenskaper som vist i eksemplet. Vi kan deretter bruke sesjonskonfigurasjonsobjektet til å instantiere en øktobjekt. Sessionsobjektet fungerer som en fabrikk for dataopplasting, opplasting og nedlastingsoppgaver, med hver oppgave som svarer til en enkelt forespørsel. I eksemplet nedenfor spør vi iTunes Search API som vi gjorde i den forrige opplæringen.

 // Opprett sesjonskonfigurasjon NSURLSessionConfiguration * sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; // Konfigurer Session Configuration [SessionConfiguration setAllowsCellularAccess: YES]; [sessionConfiguration setHTTPAdditionalHeaders: @ @ "Accept": @ "application / json"]; // Opprett økt NSURLSession * session = [NSURLSession sessionWithConfiguration: sessionConfiguration]; // Send forespørsel NSURL * url = [NSURL URLWithString: @ "https://itunes.apple.com/search?term=apple&media=software"]; [[session dataTaskWithURL: url completionHandler: ^ (NSData * data, NSURLResponse * respons, NSError * feil) NSLog (@ "% @", [NSJSONSerialization JSONObjectWithData: dataalternativer: 0 feil: null]); ] gjenoppta];

Eksemplet illustrerer også hvor enkelt det er å legge til egendefinerte overskrifter ved å sette inn HTTPAdditionalHeaders egenskapen til sesjonskonfigurasjonsobjektet. Skjønnheten i NSURLSession API er at hver forespørsel som går gjennom økten, er konfigurert av sesjonens konfigurasjonsobjekt. Hvis du legger til autentiseringshoder til et sett med forespørsler, blir det for eksempel lett som kake.


Avbryter og gjenopptar nedlastinger

I den forrige veiledningen viste jeg deg hvordan du laster ned et bilde ved hjelp av NSURLSession API. Nettverksforbindelser er imidlertid upålitelige, og det skjer altfor ofte at en nedlasting mislykkes på grunn av en flakket nettverkstilkobling. Heldigvis gjenoppta en nedlasting er ikke vanskelig med NSURLSession API. I det neste eksemplet viser jeg deg hvordan du avbryter og fortsetter nedlastingen av et bilde.

Før vi tar en nærmere titt på å gjenoppta en nedlastingsoppgave, er det viktig å forstå forskjellen mellom å kansellere og suspendere en nedlastingsoppgave. Det er mulig å suspendere en nedlastingsoppgave og gjenoppta den på et senere tidspunkt. Avbryter en nedlastingsoppgave, men stopper oppgaven, og det er ikke mulig å gjenoppta det på et senere tidspunkt. Det er imidlertid ett alternativ. Det er mulig å avbryte en nedlastingsoppgave ved å ringe cancelByProducingResumeData: på den. Den aksepterer en ferdigstillingshåndterer som aksepterer en parameter, en NSData objekt som brukes til å gjenoppta nedlastingen på et senere tidspunkt ved å påkalle downloadTaskWithResumeData: eller downloadTaskWithResumeData: completionHandler: på et økt objekt. De NSData objekt inneholder nødvendig informasjon for å gjenoppta nedlastingsoppgaven der den sluttet.

Trinn 1: Utsalgssteder og tiltak

Åpne prosjektet vi opprettet i den forrige opplæringen eller last den ned her. Vi starter med å legge to knapper til brukergrensesnittet, en for å avbryte nedlastingen og en for å gjenoppta nedlastingen. I tittelkontrollens headerfil lager du et uttak og en handling for hver knapp som vist nedenfor.

 #importere  @interface MTViewController: UIViewController @property (svak, ikkeatomisk) IBOutlet UIButton * CancelButton; @property (svak, ikkeatomisk) IBOutlet UIButton * resumeButton; @property (svak, ikkeatomisk) IBOutlet UIImageView * imageView; @property (svak, ikkeatomisk) IBOutlet UIProgressView * progressView; - (IBAction) avbryt: (id) avsender; - (IBAction) CV: (id) avsender; @slutt

Trinn 2: Brukergrensesnitt

Åpne prosjektets hoved storyboard og legg til to knapper til visningskontrollørens visning. Plasser knappene som vist på skjermbildet under og koble hver knapp med tilhørende uttak og handling.


Trinn 3: Refactoring

Vi må gjøre noe for å få alt til å fungere riktig. Åpen MTViewController.m og erklære en instansvariabel og to egenskaper. Instansvariabelen, økt, vil holde en referanse til økten vi skal bruke for å laste ned bildet.

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

Vi må også refactor the viewDidLoad metode, men først vil jeg gjerne implementere en getter-metode for økten. Implementeringen er ganske enkel som du kan se nedenfor. Vi lager et sesjonskonfigurasjonsobjekt ved hjelp av defaultSessionConfiguration fabrikkmetode og instansere øktobjektet med det. Visningsregulatoren fungerer som øktens delegat.

 - (NSURLSession *) økt if (! _Session) // Opprett sesjonskonfigurasjon NSURLSessionConfiguration * sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; // Opprett økt _session = [NSURLSession sessionWithConfiguration: sessionConfiguration delegate: self delegateQueue: null];  tilbake _session; 

Med økt Accessor implementert, viewDidLoad Metoden blir mye enklere. Vi lager en nedlastingsoppgave, som vi gjorde i den forrige opplæringen, og lagrer en referanse til oppgaven i downloadTask. Vi forteller deretter nedlastingsoppgaven til gjenoppta.

 - (void) viewDidLoad [super viewDidLoad]; // Opprett nedlastingsoppgave self.downloadTask = [self.session downloadTaskWithURL: [NSURL URLWithString: @ "http://cdn.tutsplus.com/mobile/uploads/2014/01/5a3f1-sample.jpg"]]; // Fortsett nedlastingsoppgave [self.downloadTask gjenoppta]; 

Trinn 4: Avbryt nedlastingen

De Avbryt: Handling inneholder logikken for å kansellere nedlastingsoppgaven vi nettopp har opprettet. Hvis downloadTask er ikke nil, vi ringer cancelByProducingResumeData: på oppgaven. Denne metoden aksepterer en parameter, en ferdigstillingsblokk. Fullføringsblokken tar også en parameter, en forekomst av NSData. Hvis resumeData er ikke nil, Vi lagrer en referanse til dataobjektet i kontrolleren resumeData eiendom.

Hvis en nedlastning ikke gjenopptas, er ferdigstillingsblokken resumeData parameteren er nil. Ikke alle nedlastinger gjenopptas, så det er viktig å sjekke om resumeData er gyldig NSData gjenstand.

 - (IBAction) avbryt: (id) avsender hvis (! Self.downloadTask) returnerer; // Skjul Avbryt-knapp [self.cancelButton setHidden: YES]; [self.downloadTask cancelByProducingResumeData: ^ (NSData * resumeData) hvis (! resumeData) returnerer; [self setResumeData: resumeData]; [self setDownloadTask: null]; ]; 

Trinn 5: Fortsett nedlastingen

Det er enkelt å gjenoppta nedlastingsoppgaven etter at den ble kansellert. I gjenoppta: handling, vi sjekker om visningskontrolleren er resumeData eiendommen er satt. Hvis resumeData er gyldig NSData objekt, forteller vi økt motsette seg å opprette en ny nedlastingsoppgave og sende den til NSData gjenstand. Dette er alt økt objektet må gjenskape nedlastingsoppgaven vi avbrutt i Avbryt: handling. Vi forteller deretter nedlastingsoppgaven til gjenoppta og sett resumeData til nil.

 - (IBAction) CV: (id) avsender hvis (! Self.resumeData) returnerer; // Skjul CV-knappen [self.resumeButton setHidden: YES]; // Opprett nedlastingsoppgave self.downloadTask = [self.session downloadTaskWithResumeData: self.resumeData]; // Fortsett nedlastingsoppgave [self.downloadTask gjenoppta]; // Opprydding [self setResumeData: null]; 

Bygg prosjektet og kjør programmet i iOS-simulatoren eller på en fysisk enhet. Nedlastingen skal starte automatisk. Trykk på Avbryt-knappen for å avbryte nedlastingen og trykk på Fortsett-knappen for å gjenoppta nedlastingen.

Trinn 6: Etterbehandling

Det er en rekke detaljer vi må ta vare på. Først og fremst bør knappene ikke alltid være synlige. Vi bruker nøkkelverdien å observere for å vise og skjule knappene når det er nødvendig. I viewDidLoad, skjul knappene og legg til visningskontrollen som en observatør for seg selv for resumeData og downloadTask viktige stier.

 - (void) viewDidLoad [super viewDidLoad]; // Legg til observatør [self addObserver: self forKeyPath: @ "resumeData" alternativer: NSKeyValueObservingOptionNew context: NULL]; [self addObserver: self forKeyPath: @ "downloadTask" alternativer: NSKeyValueObservingOptionNew kontekst: NULL]; // Oppsett brukergrensesnitt [self.cancelButton setHidden: YES]; [self.resumeButton setHidden: YES]; // Opprett nedlastingsoppgave self.downloadTask = [self.session downloadTaskWithURL: [NSURL URLWithString: @ "http://cdn.tutsplus.com/mobile/uploads/2014/01/5a3f1-sample.jpg"]]; // Fortsett nedlastingsoppgave [self.downloadTask gjenoppta]; 

I observeValueForKeyPath: ofObject: endring: kontekst:, vi skjuler avbryt-knappen hvis resumeData er nil og vi skjuler CV-knappen hvis downloadTask er nil. Bygg prosjektet og kjør programmet enda en gang for å se resultatet. Dette er bedre. Ikke sant?

 - (void) observeValueForKeyPath: (NSString *) keyPath ofObject: (id) objektendring: (NSDictionary *) endre kontekst: (void *) kontekst if ([keyPath isEqualToString: @ "resumeData"]) dispatch_async (dispatch_get_main_queue ^ [self.resumeButton setHidden: (self.resumeData == nil)];);  ellers hvis ([keyPath isEqualToString: @ "downloadTask"]) dispatch_async (dispatch_get_main_queue (), ^ [self.cancelButton setHidden: (self.downloadTask == nil)];); 
Som George Yang påpeker i kommentarene, vet vi ikke om observeValueForKeyPath: ofObject: endring: kontekst: kaller på hovedtråden. Det er derfor viktig å oppdatere brukergrensesnittet i en GCD (Grand Central Dispatch) -blokk som er påkalt på hovedkøen.

Trinn 7: Ugyldiggjør sesjonen

Det er et viktig aspekt av NSURLSession som jeg ikke har snakket om ennå, økt ugyldighet. Økten holder en sterk referanse til sin delegat, noe som betyr at delegaten ikke slippes så lenge sesjonen er aktiv. For å bryte denne referansesyklusen må sesjonen ugyldiggjøres. Når en økt er ugyldig, blir aktive oppgaver kansellert eller ferdig, og delegaten sendes a URLSession: didBecomeInvalidWithError: melding og økten utgir sin delegat.

Det er flere steder vi kan ugyldiggjøre økten. Siden visningskontrollen laster ned bare ett bilde, kan sesjonen ugyldiggjøres når nedlastingen er ferdig. Ta en titt på den oppdaterte implementeringen av URLSession: downloadTask: didFinishDownloadingToURL:. Avbryt-knappen er også skjult når nedlastingen er ferdig.

 - (void) URLSession: (NSURLSession *) sessionsnedlastningTask: (NSURLSessionDownloadTask *) downloadTask didFinishDownloadingToURL: (NSURL *) sted 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]];); // Invalidiser økt [Session finishTasksAndInvalidate]; 

Konklusjon

Eksemplet prosjektet vi opprettet i denne opplæringen er en forenklet implementering av hvordan du avbryter og gjenopptar nedlastinger. I dine applikasjoner kan det være nødvendig å skrive resumeData Motta disk for senere bruk, og det kan være mulig at flere nedlastingsoppgaver kjører samtidig. Selv om dette legger til kompleksitet, forblir de grunnleggende prinsippene de samme. Pass på at du forhindrer hukommelse ved å alltid ugyldiggjøre en økt som du ikke lenger trenger.