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.
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à.
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.
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.
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
.
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.
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.
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.
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
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.
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];
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]; ];
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.
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. 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];
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ù.