Connessione in rete con NSURLSession parte 2

Nel precedente tutorial, ti ho presentato NSURLSession. Ho parlato dei vantaggi che ha finito NSURLConnection e come usare NSURLSession per compiti semplici, come il recupero dei dati da un servizio web e il download di un'immagine dal web. In questo tutorial, daremo un'occhiata più da vicino alle opzioni di configurazione di NSURLSession e come annullare e riprendere un'attività di download. Abbiamo un sacco di terreno da percorrere quindi iniziamo.


Configurazione della sessione

Come abbiamo visto nel tutorial precedente, una sessione, un'istanza di NSURLSession, è un contenitore configurabile per inserire le richieste di rete. La configurazione della sessione è gestita da un'istanza di NSURLSessionConfiguration.

Un oggetto di configurazione di sessione non è altro che un dizionario di proprietà che definisce come si comporta la sessione a cui è legato. Una sessione ha un oggetto di configurazione di sessione che detta cookie, sicurezza e criteri di cache, il numero massimo di connessioni a un host, risorse e timeout di rete, ecc. Si tratta di un miglioramento significativo rispetto a NSURLConnection, che si basava su un oggetto di configurazione globale con molta meno flessibilità.

Mutabilità

Una volta che una sessione è stata creata e configurata da a NSURLSessionConfiguration Ad esempio, la configurazione della sessione non può essere modificata. Se è necessario modificare la configurazione di una sessione, è necessario creare una nuova sessione. Tieni presente che è possibile copiare la configurazione di una sessione e modificarla, ma le modifiche non hanno alcun effetto sulla sessione da cui è stata copiata la configurazione.

Configurazione predefinita

Il NSURLSessionConfiguration class fornisce tre factory builder per creare istanze di configurazioni standard, defaultSessionConfiguration, ephemeralSessionConfiguration, e backgroundSessionConfiguration. Il primo metodo restituisce una copia di configurazione di sessione predefinita oggetto, che si traduce in una sessione che si comporta in modo simile a un NSURLConnection oggetto nella sua configurazione standard. Modifica di una configurazione di sessione ottenuta tramite defaultSessionConfiguration il metodo factory non cambia la configurazione di sessione predefinita di cui è una copia.

Configurazione effimera

Un oggetto di configurazione di sessione creato richiamando il ephemeralSessionConfiguration il metodo factory garantisce che la sessione risultante non utilizzi memoria permanente per cookie, cache o credenziali. In altre parole, i cookie, le cache e le credenziali sono conservati in memoria. Le sessioni effimere sono quindi ideali se è necessario implementare la navigazione privata, cosa che semplicemente non era possibile prima dell'introduzione di NSURLSession.

Configurazione di sfondo

Il backgroundSessionConfiguration: metodo factory crea un oggetto di configurazione di sessione che consente caricamenti e download fuori processo. Le attività di caricamento e download sono gestite da un demone in background e continuano a essere eseguite anche se l'applicazione è sospesa o si blocca. Parleremo di più sulle sessioni in background più avanti in questa serie.

Configurazione della sessione

Come abbiamo visto nel tutorial precedente, la creazione di un oggetto di configurazione di sessione è semplice. Nell'esempio mostrato di seguito, ho usato il defaultSessionConfiguration metodo di fabbrica per creare un NSURLSessionConfiguration esempio. La configurazione di un oggetto di configurazione di sessione è semplice come la modifica delle sue proprietà come mostrato nell'esempio. Possiamo quindi utilizzare l'oggetto di configurazione della sessione per istanziare un oggetto di sessione. L'oggetto di sessione funge da fabbrica per le attività di dati, caricamento e download, con ogni attività corrispondente a una singola richiesta. Nell'esempio seguente, eseguiamo una query sull'API di ricerca di iTunes come abbiamo fatto nel tutorial precedente.

 // Crea configurazione sessione NSURLSessionConfiguration * sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; // Configura configurazione sessione [sessionConfiguration setAllowsCellularAccess: YES]; [sessionConfiguration setHTTPAdditionalHeaders: @ @ "Accept": @ "application / json"]; // Crea sessione NSURLSession * session = [NSURLSession sessionWithConfiguration: sessionConfiguration]; // Invia richiesta NSURL * url = [NSURL URLWithString: @ "https://itunes.apple.com/search?term=apple&media=software"]; [[session dataTaskWithURL: url completionHandler: ^ (dati NSData *, risposta NSURLResponse *, errore NSError *) NSLog (@ "% @", [NSJSONSerialization JSONObjectWithData: opzioni dati: 0 errore: nil]); ] curriculum vitae];

L'esempio mostra anche quanto sia facile aggiungere intestazioni personalizzate impostando il HTTPAdditionalHeaders proprietà dell'oggetto di configurazione della sessione. La bellezza del NSURLSession L'API è che ogni richiesta che passa attraverso la sessione è configurata dall'oggetto di configurazione della sessione. Ad esempio, l'aggiunta di intestazioni di autenticazione a un gruppo di richieste diventa facile come una torta.


Annullamento e ripristino dei download

Nel tutorial precedente, ti ho mostrato come scaricare un'immagine usando il NSURLSession API. Tuttavia, le connessioni di rete sono inaffidabili e accade troppo spesso che un download non riesca a causa di una connessione di rete instabile. Fortunatamente, riprendere un download non è difficile con NSURLSession API. Nel prossimo esempio, ti mostrerò come cancellare e riprendere il download di un'immagine.

Prima di dare un'occhiata più da vicino alla ripresa di un'attività di download, è importante capire la differenza tra l'annullamento e la sospensione di un'attività di download. È possibile sospendere un'attività di download e riprenderla in un secondo momento. L'annullamento di un'attività di download, tuttavia, arresta l'attività e non è possibile riprenderla in un secondo momento. C'è un'alternativa, però. È possibile annullare un'attività di download chiamando cancelByProducingResumeData: su di esso. Accetta un gestore di completamento che accetta un parametro, a NSData oggetto che viene utilizzato per riprendere il download in un secondo momento richiamando downloadTaskWithResumeData: o downloadTaskWithResumeData: completionHandler: su un oggetto di sessione. Il NSData oggetto contiene le informazioni necessarie per riprendere l'attività di download dal punto in cui era stata interrotta.

Passaggio 1: uscite e azioni

Apri il progetto che abbiamo creato nel precedente tutorial o scaricalo qui. Iniziamo aggiungendo due pulsanti all'interfaccia utente, uno per annullare il download e uno per riprendere il download. Nel file di intestazione del controller della vista, creare una presa e un'azione per ciascun pulsante come mostrato di seguito.

 #importare  @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) mittente; - (IBAction) riprendere: (id) mittente; @fine

Passaggio 2: Interfaccia utente

Apri lo storyboard principale del progetto e aggiungi due pulsanti alla vista del controller della vista. Posiziona i pulsanti come mostrato nella schermata qui sotto e collega ciascun pulsante con la relativa uscita e azione corrispondenti.


Passaggio 3: refactoring

Dovremo eseguire alcuni refactoring per far funzionare tutto correttamente. Aperto MTViewController.m e dichiarare una variabile di istanza e due proprietà. La variabile di istanza, sessione, manterrà un riferimento alla sessione che useremo per scaricare l'immagine.

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

Abbiamo anche bisogno di refactoring il viewDidLoad metodo, ma prima vorrei implementare un metodo getter per la sessione. La sua implementazione è piuttosto semplice come puoi vedere qui sotto. Creiamo un oggetto di configurazione della sessione usando il defaultSessionConfiguration metodo di fabbrica e istanziare l'oggetto della sessione con esso. Il controller della vista funge da delegato della sessione.

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

Con il sessione accessor implementato, il viewDidLoad il metodo diventa molto più semplice. Creiamo un'attività di download, come abbiamo fatto nel precedente tutorial, e memorizziamo un riferimento all'attività in downloadTask. Diamo quindi il compito di download a curriculum vitae.

 - (void) viewDidLoad [super viewDidLoad]; // Crea attività di download self.downloadTask = [self.session downloadTaskWithURL: [NSURL URLWithString: @ "http://cdn.tutsplus.com/mobile/uploads/2014/01/5a3f1-sample.jpg"]]; // Riprendi attività di download [self.downloadTask resume]; 

Passaggio 4: annullamento del download

Il Annulla: l'azione contiene la logica per annullare l'attività di download appena creata. Se downloadTask non è zero, Noi chiamiamo cancelByProducingResumeData: sull'attività. Questo metodo accetta un parametro, un blocco di completamento. Il blocco di completamento prende anche un parametro, un'istanza di NSData. Se resumeData non è zero, memorizziamo un riferimento all'oggetto dati nel controller di visualizzazione resumeData proprietà.

Se un download non è ripristinabile, il blocco di completamento resumeData il parametro è zero. Non tutti i download sono ripristinabili, quindi è importante controllare se resumeData è un valido NSData oggetto.

 - (IBAction) cancel: (id) sender if (! Self.downloadTask) return; // Nascondi pulsante Annulla [self.cancelButton setHidden: YES]; [self.downloadTask cancelByProducingResumeData: ^ (NSData * resumeData) if (! resumeData) return; [self setResumeData: resumeData]; [self setDownloadTask: nil]; ]; 

Passaggio 5: riprendere il download

Riprendere l'attività di download dopo che è stata annullata è facile. Nel curriculum vitae: azione, controlliamo se il controller della vista resumeData la proprietà è impostata. Se resumeData è un valido NSData oggetto, diciamo al sessione oggetto per creare una nuova attività di download e passarla NSData oggetto. Questo è tutto sessione oggetto ha bisogno di ricreare l'attività di download che abbiamo annullato nel Annulla: azione. Diamo quindi il compito di download a curriculum vitae e impostare resumeData a zero.

 - (IBAction) resume: (id) mittente if (! Self.resumeData) return; // Nascondi pulsante Riprendi [self.resumeButton setHidden: YES]; // Crea attività di download self.downloadTask = [self.session downloadTaskWithResumeData: self.resumeData]; // Riprendi attività di download [self.downloadTask resume]; // Cleanup [self setResumeData: nil]; 

Costruisci il progetto ed esegui l'applicazione in iOS Simulator o su un dispositivo fisico. Il download dovrebbe iniziare automaticamente. Toccare il pulsante Annulla per annullare il download e toccare il pulsante Riprendi per riprendere il download.

Passaggio 6: Tocchi finali

Ci sono una serie di dettagli di cui dobbiamo occuparci. Prima di tutto, i pulsanti non dovrebbero essere sempre visibili. Useremo l'osservazione dei valori chiave per mostrare e nascondere i pulsanti quando necessario. Nel viewDidLoad, nascondere i pulsanti e aggiungere il controller di visualizzazione come un osservatore di se stesso per il resumeData e downloadTask percorsi chiave.

 - (void) viewDidLoad [super viewDidLoad]; // Aggiungi Observer [self addObserver: self forKeyPath: @ "resumeData" opzioni: NSKeyValueObservingOptionNuovo contesto: NULL]; [self addObserver: self forKeyPath: @ "downloadTask" opzioni: NSKeyValueObservingOptionNuovo contesto: NULL]; // Imposta l'interfaccia utente [self.cancelButton setHidden: YES]; [self.resumeButton setHidden: YES]; // Crea attività di download self.downloadTask = [self.session downloadTaskWithURL: [NSURL URLWithString: @ "http://cdn.tutsplus.com/mobile/uploads/2014/01/5a3f1-sample.jpg"]]; // Riprendi attività di download [self.downloadTask resume]; 

Nel observeValueForKeyPath: ofObject: cambiamento: contesto:, nascondiamo il pulsante Annulla se resumeData è zero e nascondiamo il pulsante Riprendi se downloadTask è zero. Costruisci il progetto ed esegui l'applicazione ancora una volta per vedere il risultato. Questo è meglio. Destra?

 - (void) observValueForKeyPath: (NSString *) keyPath ofObject: (id) oggetto change: (NSDictionary *) change 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)];); 
Come sottolinea George Yang nei commenti, non sappiamo se observeValueForKeyPath: ofObject: cambiamento: contesto: è chiamato sul thread principale. È quindi importante aggiornare l'interfaccia utente in un blocco GCD (Grand Central Dispatch) che viene richiamato sulla coda principale.

Passaggio 7: invalidare la sessione

C'è un aspetto chiave di NSURLSession di cui non ho ancora parlato, invalidazione della sessione. La sessione mantiene un forte riferimento al suo delegato, il che significa che il delegato non viene rilasciato fino a quando la sessione è attiva. Per interrompere questo ciclo di riferimento, la sessione deve essere invalidata. Quando una sessione viene invalidata, le attività attive vengono annullate o completate e il delegato viene inviato a URLSession: didBecomeInvalidWithError: messaggio e la sessione rilascia il suo delegato.

Ci sono diversi posti in cui possiamo invalidare la sessione. Poiché il controller della vista scarica solo un'immagine, la sessione può essere invalidata al termine del download. Dai un'occhiata all'implementazione aggiornata di URLSession: downloadTask: didFinishDownloadingToURL:. Il pulsante Annulla viene inoltre nascosto al termine del download.

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

Conclusione

Il progetto di esempio che abbiamo creato in questo tutorial è un'implementazione semplificata di come annullare e riprendere i download. Nelle tue applicazioni, potrebbe essere necessario scrivere il resumeData oggetto su disco per uso futuro e potrebbe essere possibile che diverse attività di download siano in esecuzione contemporaneamente. Anche se questo aggiunge complessità, i principi di base rimangono gli stessi. Assicurati di evitare perdite di memoria invalidando sempre una sessione che non ti serve più.