Questo tutorial ti mostrerà come utilizzare il servizio di trasferimento in background, un'API multitasking fornita da iOS 7. Ti insegnerò come creare un'app che scaricherà un file senza l'applicazione in primo piano. Una volta scaricato completamente il file, verrà visualizzato un messaggio di notifica. Continua a leggere per creare questo servizio!
Il servizio di trasferimento in background è stato creato con iOS 6. Questa funzione consentiva alle app di trasferire file sia in modalità in primo piano che in background, ma limitava i minuti. Il problema più grande è stato quando i "minuti limitati" non consentivano all'utente di scaricare o caricare file di grandi dimensioni. Ecco perché Apple ha migliorato il framework in iOS 7.
Con iOS 7, questa funzione ha subito importanti cambiamenti, tra cui:
Il servizio di trasferimento in background può essere utilizzato per diverse attività distinte e utili come: caricamento di foto o video, combinazione di recupero dello sfondo e notifiche remote e per tenere aggiornata l'app, come con gli acquisti di libri, programmi TV, podcast, contenuti di gioco, mappe e altro.
Per creare questo servizio, abbiamo bisogno di una singola vista con le seguenti proprietà:
UIDocumentInterationController
(per aprire il download del documento PDF)Innanzitutto, avvia un nuovo progetto Xcode per iPhone. Quindi creare un Applicazione vista singola. Quindi, vai al Main.Storyboard
e aggiungere alcuni oggetti al nostro vista. Per aggiungere il navigationController seleziona il Default View Controller. Nel menu Xcode, selezionare Editor> Incorpora in> Controller di navigazione. Devi trascinare e rilasciare il Oggetto della barra e il Vista del progresso al tuo View Controller. Una volta che hai finito, il View Controller dovrebbe apparire simile alla seguente immagine:
Ora, aggiungiamo le proprietà necessarie per interagire con gli oggetti che abbiamo aggiunto. Nel ViewController.h
, aggiungi le seguenti righe:
@property (weak, nonatomic) IBOutlet UIProgressView * progressView; - (IBAction) start: (id) mittente;
Ora cambia la vista per il ViewController.m
. Apparirà un avvertimento, ma non preoccuparti di ciò; lo sistemeremo più tardi. Torna al Main.Storyboard
e connetti gli oggetti con le proprietà e le azioni.
Questo passaggio è banale, ma se hai qualche dubbio sentiti libero di usare la sezione commenti qui sotto.
Il NSURLSession
classi e classi correlate forniscono un'API per scaricare o caricare contenuti tramite HTTP. Questa API è responsabile della gestione di una serie di attività di trasferimento. Dovrai creare tre oggetti direttamente correlati a quella classe: uno NSURLSession
, NSURLSessionDownloadTask
, e UIDocumentInteractionController
.
Il tuo ViewController.h
sarà qualcosa del genere:
@property (nonatomic) NSURLSession * session; @property (nonatomic) NSURLSessionDownloadTask * downloadTask; @property (strong, nonatomic) UIDocumentInteractionController * documentInteractionController;
Inoltre, dichiarerai anche quattro protocolli: NSURLSessionDelegate
, NSURLSessionTaskDelegate
, NSURLSessionDownloadDelegate
, e UIDocumentInteractionControllerDelegate
. Il tuo @interfaccia
dovrebbe assomigliare a:
@interface ViewController: UIViewController < NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDownloadDelegate,UIDocumentInteractionControllerDelegate>
Le proprietà che aggiungeremo sono utili per istanziare e manipolare la nostra sessione e il processo di download, che ti consentirà di riprendere, sospendere, annullare o recuperare lo stato. L'ultima proprietà, UIDocumentInterationController
è usato per presentare il documento PDF scaricato in questo tutorial.
Ora vai a ViewController.m
.
La prima attività da completare consiste nell'aggiungere una stringa alla posizione del file da scaricare. Dovresti usare un PDF Apple standard.
static NSString * DownloadURLString = @ "https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/ObjC_classic/FoundationObjC.pdf";
Sul viewDidLoad
metodo, istanziamo e impostiamo sia la sessione che le viste di avanzamento:
self.session = [self backgroundSession]; self.progressView.progress = 0; self.progressView.hidden = YES;
Dal momento che chiami il backgroundSession
metodo, e non esiste, devi dichiararlo ora. Questo metodo è responsabile per l'invio di un'azione che sarà la sessione in background. Il backgroundSession
tramite la dispatch_once
esegue un blocco una volta per l'intera durata dell'applicazione. Il NSString ricevuto dal NSURLSessionConfiguration
rappresenta l'ID della nostra sessione. Questo ID deve essere univoco per ciascuna istanza NSURLSession.
Il metodo completo è il seguente:
- (NSURLSession *) backgroundSession static NSURLSession * session = nil; static dispatch_once_t onceToken; dispatch_once (& onceToken, ^ NSURLSessionConfiguration * configuration = [NSURLSessionConfiguration backgroundSessionConfiguration: @ "com.example.apple-samplecode.SimpleBackgroundTransfer.BackgroundSession"]; session = [NSURLSession sessionWithConfiguration: delegato di configurazione: self delegateQueue: nil];); sessione di ritorno;
Il (IBAction) inizio: (id) sender
metodo avvia il download del documento. Avvierai quindi un NSURL
e a NSURLRequest
e usa il downloadTask
proprietà per passare l'oggetto richiesta al downloadTaskWithRequest
metodo. Il metodo completo è di seguito.
- (IBAction) start: (id) sender if (self.downloadTask) return; NSURL * downloadURL = [NSURL URLWithString: DownloadURLString]; NSURLRequest * request = [NSURLRequequest requestWithURL: downloadURL]; self.downloadTask = [self.session downloadTaskWithRequest: request]; [self.downloadTask resume]; self.progressView.hidden = NO;
A questo punto, noterai che sono presenti tre avvisi. Affermano che il metodo di protocollo dovrebbe essere implementato. Il NSURLSessionDownloadDelegate
protocollo definisce i metodi per gestire l'attività di download. Per eseguire il download, è necessario utilizzare i tre metodi delegati.
Quindi, aggiungere i seguenti tre metodi:
(void) URLSession: (NSURLSession *) session downloadTask: (NSURLSessionDownloadTask *) downloadTask didWriteData: (int64_t) bytesWritten totalBytesWritten: (int64_t) totalBytesWritten totalBytesExpectedToWrite: (int64_t) totalBytesExpectedToWrite
(void) URLSession: (NSURLSession *) sessione downloadTask: (NSURLSessionDownloadTask *) downloadTask didFinishDownloadingToURL: (NSURL *) downloadURL
(void) URLSession: (NSURLSession *) sessione downloadTask: (NSURLSessionDownloadTask *) downloadTask didResumeAtOffset: (int64_t) fileOffset expectedTotalBytes: (int64_t) expectedTotalBytes
Il (void) URLSession: (NSURLSession *) sessione downloadTask: (NSURLSessionDownloadTask *) downloadTask didWriteData: (int64_t) bytesWritten totalBytesWritten: (int64_t) totalBytesWritten totalBytesExpectedToWrite: (int64_t) totalBytesExpectedToWrite
il metodo è responsabile per tenere traccia del processo di download generale. Aggiorna anche il progressView
di conseguenza. Il metodo completo è di seguito.
- (void) URLSession: (NSURLSession *) sessione downloadTask: (NSURLSessionDownloadTask *) downloadTask didWriteData: (int64_t) bytesWritten totalBytesWritten: (int64_t) totalBytesWritten totalBytesExpectedToWrite: (int64_t) totalBytesExpectedToWrite if (downloadTask == self.downloadTask) double progress = ( double) totalBytesWritten / (double) totalBytesExpectedToWrite; NSLog (@ "DownloadTask:% @ progress:% lf", downloadTask, progress); dispatch_async (dispatch_get_main_queue (), ^ self.progressView.progress = progress;);
Il (void) URLSession: (NSURLSession *) sessione downloadTask: (NSURLSessionDownloadTask *) downloadTask didFinishDownloadingToURL: (NSURL *) downloadURL
il metodo si occupa dei dati stessi (origine e destinazione). Controlla il file solo quando è completamente scaricato. Per dirla in modo semplice, dice al delegato che l'attività di download è terminata con il download. Contiene l'attività di sessione terminata, l'attività di download terminata e un URL di file in cui è possibile trovare il file temporaneo. Dovrebbe sembrare come questo:
- (void) URLSession: (NSURLSession *) sessione downloadTask: (NSURLSessionDownloadTask *) downloadTask didFinishDownloadingToURL: (NSURL *) downloadURL NSFileManager * fileManager = [NSFileManager defaultManager]; URL NSArray * [fileManager URLsForDirectory: NSDocumentDirectory inDomains: NSUserDomainMask]; NSURL * documentsDirectory = [URL objectAtIndex: 0]; NSURL * originalURL = [[downloadTask originalRequest] URL]; NSURL * destinationURL = [documentsDirectory URLByAppendingPathComponent: [originalURL lastPathComponent]]; NSError * errorCopy; // Ai fini del test, rimuovere qualsiasi file esistente nella destinazione. [fileManager removeItemAtURL: destinationURL error: NULL]; BOOL successo = [fileManager copyItemAtURL: downloadURL toURL: destinationURL error: & errorCopy]; if (successo) dispatch_async (dispatch_get_main_queue (), ^ // download terminato - apre il pdf self.documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL: destinationURL]; // Configura Document Interaction Controller [self.documentInteractionController setDelegate: self]; // Preview PDF [self.documentInteractionController presentPreviewAnimated: YES]; self.progressView.hidden = YES;); else NSLog (@ "Errore durante la copia:% @", [errorCopy localizedDescription]);
Finalmente, il (void) URLSession: (NSURLSession *) sessione downloadTask: (NSURLSessionDownloadTask *) downloadTask didResumeAtOffset: (int64_t) fileOffset expectedTotalBytes: (int64_t) expectedTotalBytes
deve anche essere dichiarato. Ma sappi che non lo useremo più.
-(void) URLSession: (NSURLSession *) sessione downloadTask: (NSURLSessionDownloadTask *) downloadTask didResumeAtOffset: (int64_t) fileOffset expectedTotalBytes: (int64_t) expectedTotalBytes
Hai quasi finito con questa classe, restano solo due metodi: (void) URLSession: (NSURLSession *) task di sessione: (NSURLSessionTask *) task didCompleteWithError: (NSError *) errore
, e - (void) URLSessionDidFinishEventsForBackgroundURLSession: sessione (NSURLSession *)
.
Il primo metodo informa il delegato che l'attività ha completato il trasferimento dei dati. Si dovrebbe anche usarlo per tracciare qualsiasi errore che si verifica.
- (void) URLSession: (NSURLSession *) task di sessione: (NSURLSessionTask *) task doneCompleteWithError: (NSError *) error if (error == nil) NSLog (@ "Task:% @ completato correttamente", task); else NSLog (@ "Task:% @ completato con errore:% @", task, [error localizedDescription]); double progress = (double) task.countOfBytesReceived / (double) task.countOfBytesExpectedToReceive; dispatch_async (dispatch_get_main_queue (), ^ self.progressView.progress = progress;); self.downloadTask = nil;
Infine, è necessario aggiungere il (void) URLSessionDidFinishEventsForBackgroundURLSession: sessione (NSURLSession *)
metodo. Indica al delegato che sono stati consegnati tutti i messaggi accodati per una sessione. Ti istanzia il tuo AppDelegate
al fine di lanciare a UILocalNotification
. Il metodo che devi usare è:
- (void) URLSessionDidFinishEventsForBackgroundURLSession: (NSURLSession *) session AppDelegate * appDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate]; if (appDelegate.backgroundSessionCompletionHandler) void (^ completionHandler) () = appDelegate.backgroundSessionCompletionHandler; appDelegate.backgroundSessionCompletionHandler = nil; completionHandler (); NSLog (@ "Tutte le attività sono finite");
Verranno visualizzati diversi errori perché non hai ancora importato AppDelegate.h nella tua classe.
#import "AppDelegate.h"
Sposta in AppDelegate.h
e aggiungi i seguenti due oggetti:
@property (strong, nonatomic) finestra UIWindow *; @property (copy) void (^ backgroundSessionCompletionHandler) ();
Nel AppDelegate.m
dovresti implementare il metodo delegato (void) application: (UIApplication *) application handleEventsForBackgroundURLSession: (NSString *) identificatore completionHandler: (void (^) ()) completionHandler
. Indica al delegato che gli eventi relativi a una sessione URL sono in attesa di essere elaborati e chiama un metodo personalizzato (presentNotification
) per avvisare l'utente quando il file viene scaricato completamente. Il metodo completo è:
- (void) application: (UIApplication *) application handleEventsForBackgroundURLSession: (NSString *) identificatore completionHandler: (void (^) ()) completionHandler self.backgroundSessionCompletionHandler = completionHandler; // aggiungi notifica [auto presenteNotifica];
Il presentNotification
il metodo usa il UILocalNotification
classe per creare una notifica locale. Crea un suono e usa il sistema di badge per quella notifica. Ecco il metodo completo:
-(void) presentNotification UILocalNotification * localNotification = [[UILocalNotification alloc] init]; localNotification.alertBody = @ "Download completato!"; localNotification.alertAction = @ "Download trasferimento in background!"; // Su sound localNotification.soundName = UILocalNotificationDefaultSoundName; // aumenta il numero di badge dell'applicazione più 1 localNotification.applicationIconBadgeNumber = [[UIApplication sharedApplication] applicationIconBadgeNumber] + 1; [[UIApplication sharedApplication] presentLocalNotificationNow: localNotification];
A proposito, per riavviare il contatore in alto a sinistra sull'icona dell'app, è necessario aggiungere la seguente riga al (void) applicationDidBecomeActive: (applicazione UIApplication *)
metodo nel AppDelegate
.
application.applicationIconBadgeNumber = 0;
La notifica dovrebbe essere simile alla seguente immagine:
Adesso è tempo di Correre
l'app e testare il download in background.
Alla fine di questo tutorial, dovresti aver completato il tuo servizio di trasferimento in background in iOS 7. Dovresti capire il servizio di trasferimento in background e come implementarlo. Se avete domande, per favore lasciatele nella sezione commenti qui sotto.