iOS 8 dati di base e recupero asincrono

Nel precedente articolo su iOS 8 e Core Data, abbiamo discusso degli aggiornamenti in batch. Gli aggiornamenti batch non sono l'unica nuova API in città. A partire da iOS 8 e OS X Yosemite, è possibile recuperare i dati in modo asincrono. In questo tutorial, daremo un'occhiata più da vicino a come implementare il recupero asincrono e in quali situazioni l'applicazione può trarre vantaggio da questa nuova API.

1. Il problema

Come gli aggiornamenti in batch, il recupero asincrono è stato nella lista dei desideri di molti sviluppatori per un bel po 'di tempo. Le richieste di recupero possono essere complesse, richiedendo una quantità di tempo non trascurabile da completare. Durante questo periodo la richiesta di recupero blocca il thread su cui è in esecuzione e, di conseguenza, blocca l'accesso al contesto dell'oggetto gestito che esegue la richiesta di recupero. Il problema è semplice da capire, ma come appare la soluzione di Apple.

2. La soluzione

La risposta di Apple a questo problema è il recupero asincrono. Una richiesta di recupero asincrona viene eseguita in background. Ciò significa che non blocca altre attività mentre è in esecuzione, come ad esempio l'aggiornamento dell'interfaccia utente sul thread principale.

Il recupero asincrono presenta anche altre due funzioni utili, la notifica dei progressi e la cancellazione. Una richiesta di recupero asincrona può essere annullata in qualsiasi momento, ad esempio, quando l'utente decide che la richiesta di recupero richiede troppo tempo per essere completata. La segnalazione di avanzamento è un'aggiunta utile per mostrare all'utente lo stato corrente della richiesta di recupero.

Il recupero asincrono è un'API flessibile. Non solo è possibile annullare una richiesta di recupero asincrona, è anche possibile apportare modifiche al contesto dell'oggetto gestito mentre viene eseguita la richiesta di recupero asincrono. In altre parole, l'utente può continuare a utilizzare l'applicazione mentre l'applicazione esegue una richiesta di recupero asincrona in background.

3. Come funziona?

Come gli aggiornamenti batch, le richieste di recupero asincrono vengono passate al contesto dell'oggetto gestito come a NSPersistentStoreRequest oggetto, un'istanza del NSAsynchronousFetchRequest classe per essere precisi.

Un NSAsynchronousFetchRequest l'istanza è inizializzata con un NSFetchRequest oggetto e un blocco di completamento. Il blocco di completamento viene eseguito quando la richiesta di recupero asincrona ha completato la richiesta di recupero.

Rivediamo l'applicazione da fare creata in precedenza in questa serie e sostituiamo l'attuale implementazione di NSFetchedResultsController classe con una richiesta di recupero asincrona.

Passaggio 1: Impostazione del progetto

Scarica o clona il progetto da GitHub e aprilo in Xcode 6. Prima di iniziare a lavorare con NSAsynchronousFetchRequest classe, abbiamo bisogno di apportare alcune modifiche. Non saremo in grado di utilizzare il NSFetchedResultsController classe per la gestione dei dati della vista tabella dal NSFetchedResultsController la classe è stata progettata per essere eseguita sul thread principale.

Passaggio 2: sostituzione del controller dei risultati recuperati

Inizia aggiornando l'estensione di classe privata del file TSPViewController classe come mostrato di seguito. Rimuoviamo il fetchedResultsController proprietà e creare una nuova proprietà, elementi, di tipo NSArray per memorizzare gli oggetti da fare. Questo significa anche che il TSPViewController la classe non ha più bisogno di conformarsi al NSFetchedResultsControllerDelegate protocollo.

@interface TSPViewController () @property (strong, nonatomic) NSArray * elementi; @property (strong, nonatomic) Selezione NSIndexPath *; @fine

Prima di refactoring il viewDidLoad metodo, voglio prima aggiornare l'implementazione del UITableViewDataSource protocollo. Dai un'occhiata alle modifiche che ho apportato nei seguenti blocchi di codice.

- (NSInteger) numberOfSectionsInTableView: (UITableView *) tableView return self.items? 1: 0; 
- (NSInteger) tableView: (UITableView *) tableView numberOfRowsInSection: (NSInteger) section return self.items? self.items.count: 0; 
- (void) configureCell: (TSPToDoCell *) cell atIndexPath: (NSIndexPath *) indexPath // Recupera record NSManagedObject * record = [self.items objectAtIndex: indexPath.row]; // Aggiorna cella [cell.nameLabel setText: [record valueForKey: @ "nome"]]; [cell.doneButton setSelected: [[record valueForKey: @ "done"] boolValue]]; [cell setDidTapButtonBlock: ^ BOOL isDone = [[record valueForKey: @ "done"] boolValue]; // Aggiorna record [record setValue: @ (! IsDone) forKey: @ "done"]; ]; 
- (void) tableView: (UITableView *) tableView commitEditingStyle: (UITableViewCellEditingStyle) editStyle forRowAtIndexPath: (NSIndexPath *) indexPath if (editStyle == UITableViewCellEditingStyleDelete) NSManagedObject * record = [self.items objectAtIndex: indexPath.row]; if (record) [self.managedObjectContext deleteObject: record]; 

Abbiamo anche bisogno di cambiare una riga di codice nel prepareForSegue: mittente: metodo come mostrato di seguito.

// Fetch Record NSManagedObject * record = [self.items objectAtIndex: self.selection.row];

Ultimo ma non meno importante, eliminare l'implementazione del NSFetchedResultsControllerDelegate protocollo poiché non ne abbiamo più bisogno.

Passaggio 3: creazione della richiesta di recupero asincrona

Come puoi vedere di seguito, creiamo la richiesta di recupero asincrona nel controller della vista viewDidLoad metodo. Prendiamo un momento per vedere cosa sta succedendo.

- (void) viewDidLoad [super viewDidLoad]; // Helpers __weak TSPViewController * weakSelf = self; // Inizializza Richiesta di recupero NSFetchRequest * fetchRequest = [[NSFetchRequest alloc] initWithEntityName: @ "TSPItem"]; // Aggiungi ordinamento descrittori [fetchRequest setSortDescriptors: @ [[NSSortDescriptor sortDescriptorWithKey: @ "createdAt" ascendente: YES]]]; // Initialize Asynchronous Fetch Request NSAsynchronousFetchRequest * asynchronousFetchRequest = [[NSAsynchronousFetchRequest alloc] initWithFetchRequest: fetchRequest completionBlock: ^ (NSAsynchronousFetchResult * result) dispatch_async (dispatch_get_main_queue (), ^ // Processo asincrono Fetch Risultato [weakSelf processAsynchronousFetchResult: result];) ; ]; // Esegui richiesta di recupero asincrona [self.managedObjectContext performBlock: ^ // Esegui richiamo asincrono Richiesta NSError * asynchronousFetchRequestError = nil; NSAsynchronousFetchResult * asynchronousFetchResult = (NSAsynchronousFetchResult *) [weakSelf.managedObjectContext executeRequest: asynchronousFetchRequest error: & asynchronousFetchRequestError]; if (asynchronousFetchRequestError) NSLog (@ "Impossibile eseguire il risultato del recupero asincrono."); NSLog (@ "% @,% @", asynchronousFetchRequestError, asynchronousFetchRequestError.localizedDescription); ]; 

Iniziamo creando e configurando un NSFetchRequest istanza per inizializzare la richiesta di recupero asincrona. È questa richiesta di recupero che la richiesta di recupero asincrono verrà eseguita in background.

// Inizializza Richiesta di recupero NSFetchRequest * fetchRequest = [[NSFetchRequest alloc] initWithEntityName: @ "TSPItem"]; // Aggiungi ordinamento descrittori [fetchRequest setSortDescriptors: @ [[NSSortDescriptor sortDescriptorWithKey: @ "createdAt" ascendente: YES]]];

Per inizializzare un NSAsynchronousFetchRequest esempio, invochiamo initWithFetchRequest: completionBlock:, passando dentro fetchRequest e un blocco di completamento.

// Initialize Asynchronous Fetch Request NSAsynchronousFetchRequest * asynchronousFetchRequest = [[NSAsynchronousFetchRequest alloc] initWithFetchRequest: fetchRequest completionBlock: ^ (NSAsynchronousFetchResult * result) dispatch_async (dispatch_get_main_queue (), ^ // Processo asincrono Fetch Risultato [weakSelf processAsynchronousFetchResult: result];) ; ];

Il blocco di completamento viene richiamato quando la richiesta di recupero asincrona ha completato l'esecuzione della sua richiesta di recupero. Il blocco di completamento accetta un argomento di tipo NSAsynchronousFetchResult, che contiene il risultato della query e un riferimento alla richiesta di recupero asincrona originale.

Nel blocco di completamento, invochiamo processAsynchronousFetchResult:, passando nel NSAsynchronousFetchResult oggetto. Daremo un'occhiata a questo metodo di supporto in pochi istanti.

L'esecuzione della richiesta di recupero asincrona è quasi identica a come eseguiamo un NSBatchUpdateRequest. Noi chiamiamo ExecuteRequest: errore: nel contesto dell'oggetto gestito, passando la richiesta di recupero asincrona e un puntatore a un NSError oggetto.

[self.managedObjectContext performBlock: ^ // Esegui richiamo asincrono Richiesta NSError * asynchronousFetchRequestError = nil; NSAsynchronousFetchResult * asynchronousFetchResult = (NSAsynchronousFetchResult *) [weakSelf.managedObjectContext executeRequest: asynchronousFetchRequest error: & asynchronousFetchRequestError]; if (asynchronousFetchRequestError) NSLog (@ "Impossibile eseguire il risultato del recupero asincrono."); NSLog (@ "% @,% @", asynchronousFetchRequestError, asynchronousFetchRequestError.localizedDescription); ];

Si noti che eseguiamo la richiesta di recupero asincrona chiamando performBlock: sul contesto dell'oggetto gestito. Mentre questo non è strettamente necessario dal momento che viewDidLoad metodo, in cui creiamo ed eseguiamo la richiesta di recupero asincrona, viene chiamato sul thread principale, è buona abitudine e buona pratica farlo.

Anche se la richiesta di recupero asincrona viene eseguita in background, si noti che il ExecuteRequest: errore: metodo restituisce immediatamente, consegnandoci un NSAsynchronousFetchResult oggetto. Una volta completata la richiesta di recupero asincrono, lo stesso NSAsynchronousFetchResult l'oggetto viene popolato con il risultato della richiesta di recupero.

Infine, controlliamo se la richiesta di recupero asincrono è stata eseguita senza problemi controllando se il NSError l'oggetto è uguale a zero.

Passaggio 4: elaborazione del risultato di raccolta asincrona

Il processAsynchronousFetchResult: metodo non è altro che un metodo di supporto in cui viene elaborato il risultato della richiesta di recupero asincrona. Impostiamo il controller della vista elementi proprietà con il contenuto del risultato risultato finale proprietà e ricaricare la vista tabella.

- (void) processAsynchronousFetchResult: (NSAsynchronousFetchResult *) asynchronousFetchResult if (asynchronousFetchResult.finalResult) // Elementi di aggiornamento [self setItems: asynchronousFetchResult.finalResult]; // Reload Table View [self.tableView reloadData]; 

Passaggio 5: costruisci ed esegui

Costruisci il progetto ed esegui l'applicazione in iOS Simulator. Potresti rimanere sorpreso nel vedere la tua applicazione bloccarsi quando tenta di eseguire la richiesta di recupero asincrona. Fortunatamente, l'output nella console ci dice cosa è andato storto.

*** Termina l'applicazione a causa dell'eccezione non rilevata 'NSInvalidArgumentException', motivo: 'contesto NSConfinementConcurrencyType  non può supportare la richiesta di recupero asincrona  con richiesta di recupero  (entità: TSPItem; predicato: ((null)); sortDescriptors: (("(createdAt, ascending, compare :)")); type: NSManagedObjectResultType;). '

Se non hai letto l'articolo sui Core Data e la concorrenza, potresti essere confuso da ciò che stai leggendo. Ricorda che i Core Data dichiarano tre tipi di concorrenza, NSConfinementConcurrencyTypeNSPrivateQueueConcurrencyType, e NSMainQueueConcurrencyType. Ogni volta che crei un contesto oggetto gestito richiamando la classe dentro metodo, il tipo di concomitanza del contesto oggetto gestito risultante è uguale a NSConfinementConcurrencyType. Questo è il tipo di concorrenza predefinito.

Il problema, tuttavia, è che il recupero asincrono non è compatibile con NSConfinementConcurrencyType genere. Senza entrare troppo nel dettaglio, è importante sapere che la richiesta di recupero asincrona deve unire i risultati della sua richiesta di recupero con il contesto dell'oggetto gestito che ha eseguito la richiesta di recupero asincrona. Ha bisogno di sapere su quale coda di invio può fare questo ed è per questo che solo NSPrivateQueueConcurrencyTypeNSMainQueueConcurrencyType supporta il recupero asincrono. La soluzione è molto semplice però.

Passaggio 6: configurazione del contesto dell'oggetto gestito

Aperto TSPAppDelegate.m e aggiorna il managedObjectContext metodo come mostrato di seguito.

- (NSManagedObjectContext *) managedObjectContext if (_managedObjectContext) return _managedObjectContext;  NSPersistentStoreCoordinator * coordinator = [self persistentStoreCoordinator]; if (coordinator) _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType: NSMainQueueConcurrencyType]; [_managedObjectContext setPersistentStoreCoordinator: coordinator];  return _managedObjectContext; 

L'unico cambiamento che abbiamo fatto è sostituire il dentro metodo con initWithConcurrencyType:, passando dentro NSMainQueueConcurrencyType come argomento. Ciò significa che è necessario accedere al contesto dell'oggetto gestito solo dal thread principale. Funziona bene finché usiamo il performBlock: o performBlockAndWait: metodi per accedere al contesto dell'oggetto gestito.

Esegui il progetto ancora una volta per assicurarti che il nostro cambiamento abbia effettivamente risolto il problema.

4. Mostrare il progresso

Il NSAsynchronousFetchRequest la classe aggiunge il supporto per monitorare lo stato di avanzamento della richiesta di recupero ed è anche possibile cancellare una richiesta di recupero asincrona, ad esempio, se l'utente decide che ci vuole troppo tempo per completare.

Il NSAsynchronousFetchRequest classe sfrutta il NSProgress classe per la segnalazione dei progressi e cancellazione di una richiesta di recupero asincrona. Il NSProgress classe, disponibile da iOS 7 e OS X 10.9, è un modo intelligente per monitorare l'avanzamento di un'attività senza la necessità di accoppiare strettamente l'attività all'interfaccia utente.

Il NSProgress la classe supporta anche l'annullamento, che è come può essere cancellata una richiesta di recupero asincrona. Scopriamo cosa dobbiamo fare per implementare la segnalazione dei progressi per la richiesta di recupero asincrono.

Passaggio 1: aggiunta di SVProgressHUD

Mostreremo all'utente il progresso della richiesta di recupero asincrona usando la libreria SVProgressHUD di Sam Vermette. Scarica la libreria da GitHub e aggiungi il SVProgressHUD cartella per il tuo progetto Xcode.

Passaggio 2: impostazione NSProgress 

In questo articolo, non esploreremo il NSProgress classe in molti dettagli, ma sentiti libero di leggere di più su di esso nella documentazione. Creiamo un NSProgress istanza nel blocco che consegniamo al performBlock: metodo nel controller della vista viewDidLoad metodo.

// Crea avanzamento NSProgress * progresso = [NSProgress progressWithTotalUnitCount: 1]; // Diventa corrente [progress becomeCurrentWithPendingUnitCount: 1];

Potresti essere sorpreso di impostare il conteggio totale delle unità su 1. La ragione è semplice. Quando Core Data esegue la richiesta di recupero asincrona, non sa quanti record troverà nell'archivio permanente. Ciò significa anche che non saremo in grado di mostrare il progresso relativo all'utente - una percentuale. Invece, mostreremo all'utente il progresso assoluto: il numero di record che ha trovato.

È possibile risolvere questo problema eseguendo una richiesta di recupero per recuperare il numero di record prima di eseguire la richiesta di recupero asincrono. Preferisco non farlo, però, perché questo significa anche che il recupero dei record dall'archivio permanente richiede più tempo per essere completato a causa della richiesta di recupero extra all'inizio.

Passaggio 3: aggiunta di un osservatore

Quando eseguiamo la richiesta di recupero asincrona, ci viene immediatamente data una NSAsynchronousFetchResult oggetto. Questo oggetto ha a progresso proprietà, che è di tipo NSProgress. È questo progresso proprietà che dobbiamo osservare se vogliamo ricevere aggiornamenti di progresso.

// Esegui richiesta di recupero asincrona [self.managedObjectContext performBlock: ^ // Crea avanzamento NSProgress * progresso = [NSProgress progressWithTotalUnitCount: 1]; // Diventa corrente [progress becomeCurrentWithPendingUnitCount: 1]; // Esegui richiamo asincrono Richiesta NSError * asynchronousFetchRequestError = nil; NSAsynchronousFetchResult * asynchronousFetchResult = (NSAsynchronousFetchResult *) [self.managedObjectContext executeRequest: asynchronousFetchRequest error: & asynchronousFetchRequestError]; if (asynchronousFetchRequestError) NSLog (@ "Impossibile eseguire il risultato del recupero asincrono."); NSLog (@ "% @,% @", asynchronousFetchRequestError, asynchronousFetchRequestError.localizedDescription);  // Aggiungi Observer [asynchronousFetchResult.progress addObserver: self forKeyPath: @ "completeUnitCount" opzioni: NSKeyValueObservingOptionNew context: ProgressContext]; // Ridisegna corrente [progresso resignCorrente]; ];

Nota che chiamiamo resignCurrent sul progresso oggetto per bilanciare il precedente becomeCurrentWithPendingUnitCount: chiamata. Tieni presente che entrambi questi metodi devono essere richiamati sullo stesso thread.

Passaggio 4: rimozione dell'osservatore

Nel blocco di completamento della richiesta di recupero asincrona, rimuoviamo l'osservatore e ignoriamo l'HUD di avanzamento.

// Initialize Asynchronous Fetch Request NSAsynchronousFetchRequest * asynchronousFetchRequest = [[NSAsynchronousFetchRequest alloc] initWithFetchRequest: fetchRequest completionBlock: ^ (NSAsynchronousFetchResult * result) dispatch_async (dispatch_get_main_queue (), ^ // Ignora avanzamento HUD [SVProgressHUD respinto]; // Process Asynchronous Fetch Risultato [weakSelf processAsynchronousFetchResult: result]; // Remove Observer [result.progress removeObserver: weakSelf forKeyPath: @ "completeUnitCount" context: ProgressContext];); ];

Prima di implementare observeValueForKeyPath: ofObject: cambiamento: contesto:, abbiamo bisogno di aggiungere una dichiarazione di importazione per la libreria SVProgressHUD, dichiarare la variabile statica ProgressContext che passiamo come contesto durante l'aggiunta e la rimozione dell'osservatore e mostriamo l'HUD di avanzamento prima di creare la richiesta di recupero asincrona.

#import "SVProgressHUD / SVProgressHUD.h"
static void * ProgressContext = & ProgressContext;
- (void) viewDidLoad [super viewDidLoad]; // Helpers __weak TSPViewController * weakSelf = self; // Show Progress HUD [SVProgressHUD showWithStatus: @ "Fetching Data" maskType: SVProgressHUDMaskTypeGradient]; // Inizializza Richiesta di recupero NSFetchRequest * fetchRequest = [[NSFetchRequest alloc] initWithEntityName: @ "TSPItem"]; // Aggiungi ordinamento descrittori [fetchRequest setSortDescriptors: @ [[NSSortDescriptor sortDescriptorWithKey: @ "createdAt" ascendente: YES]]]; // Initialize Asynchronous Fetch Request NSAsynchronousFetchRequest * asynchronousFetchRequest = [[NSAsynchronousFetchRequest alloc] initWithFetchRequest: fetchRequest completionBlock: ^ (NSAsynchronousFetchResult * result) dispatch_async (dispatch_get_main_queue (), ^ // Ignora avanzamento HUD [SVProgressHUD respinto]; // Process Asynchronous Fetch Risultato [weakSelf processAsynchronousFetchResult: result]; // Remove Observer [result.progress removeObserver: weakSelf forKeyPath: @ "completeUnitCount" context: ProgressContext];); ]; // Esegui richiesta di recupero asincrona [self.managedObjectContext performBlock: ^ // Crea avanzamento NSProgress * progresso = [NSProgress progressWithTotalUnitCount: 1]; // Diventa corrente [progress becomeCurrentWithPendingUnitCount: 1]; // Esegui richiamo asincrono Richiesta NSError * asynchronousFetchRequestError = nil; NSAsynchronousFetchResult * asynchronousFetchResult = (NSAsynchronousFetchResult *) [weakSelf.managedObjectContext executeRequest: asynchronousFetchRequest error: & asynchronousFetchRequestError]; if (asynchronousFetchRequestError) NSLog (@ "Impossibile eseguire il risultato del recupero asincrono."); NSLog (@ "% @,% @", asynchronousFetchRequestError, asynchronousFetchRequestError.localizedDescription);  // Aggiungi Observer [asynchronousFetchResult.progress addObserver: self forKeyPath: @ "completeUnitCount" opzioni: NSKeyValueObservingOptionNew context: ProgressContext]; // Ridisegna corrente [progresso resignCorrente]; ]; 

Passaggio 5: Rapporti sui progressi

Tutto ciò che resta da fare per noi è implementare il observeValueForKeyPath: ofObject: cambiamento: contesto: metodo. Controlliamo se contesto è uguale a ProgressContext, creare un stato oggetto estraendo il numero di record completati dal file modificare dizionario e aggiorna l'HUD del progresso. Si noti che aggiorniamo l'interfaccia utente sul thread principale.

- (void) observValueForKeyPath: (NSString *) keyPath ofObject: (id) modifica dell'oggetto: (NSDictionary *) modifica contesto: (void *) context if (context == ProgressContext) dispatch_async (dispatch_get_main_queue (), ^ // Create Status NSString * status = [NSString stringWithFormat: @ "Recuperato% li Records", (long) [[change objectForKey: @ "new"] integerValue]]; // Show Progress HUD [SVProgressHUD setStatus: status];); 

5. Dati fittizi

Se vogliamo testare correttamente la nostra applicazione, abbiamo bisogno di più dati. Anche se non è consigliabile utilizzare il seguente approccio in un'applicazione di produzione, è un modo semplice e rapido per popolare il database con i dati.

Aperto TSPAppDelegate.m e aggiorna il applicazione: didFinishLaunchingWithOptions: metodo come mostrato di seguito. Il populateDatabase metodo è un semplice metodo di supporto in cui aggiungiamo dati fittizi al database.

- Applicazione (BOOL): applicazione (UIApplication *) didFinishLaunchingWithOptions: (NSDictionary *) launchOptions // Popola database [self populateDatabase]; ... return YES; 

L'implementazione è semplice. Poiché vogliamo inserire solo i dati fittizi una volta, controlliamo il database dei valori predefiniti dell'utente per la chiave @ "DidPopulateDatabase". Se la chiave non è impostata, inseriremo i dati fittizi.

- (void) populateDatabase // Helpers NSUserDefaults * ud = [NSUserDefaults standardUserDefaults]; if ([ud objectForKey: @ "didPopulateDatabase"]) return; per (NSInteger i = 0; i < 1000000; i++)  // Create Entity NSEntityDescription *entity = [NSEntityDescription entityForName:@"TSPItem" inManagedObjectContext:self.managedObjectContext]; // Initialize Record NSManagedObject *record = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:self.managedObjectContext]; // Populate Record [record setValue:[NSString stringWithFormat:@"Item %li", (long)i] forKey:@"name"]; [record setValue:[NSDate date] forKey:@"createdAt"];  // Save Managed Object Context [self saveManagedObjectContext]; // Update User Defaults [ud setBool:YES forKey:@"didPopulateDatabase"]; 

 Il numero di record è importante. Se si prevede di eseguire l'applicazione su iOS Simulator, è possibile inserire 100.000 o 1.000.000 di record. Ciò non funzionerà altrettanto bene su un dispositivo fisico e richiederà troppo tempo per essere completato.

Nel per loop, creiamo un oggetto gestito e lo popoliamo con i dati. Si noti che non salviamo le modifiche del contesto dell'oggetto gestito durante ogni iterazione di per ciclo continuo.

Infine, aggiorniamo il database dei default dell'utente per assicurarci che il database non venga popolato la prossima volta che l'applicazione viene lanciata.

Grande. Esegui l'applicazione in iOS Simulator per vedere il risultato. Noterai che ci vogliono alcuni istanti prima che la richiesta di recupero asincrona inizi a recuperare i record e aggiorni l'HUD di avanzamento.

6. Ultime modifiche

Sostituendo la classe del controller dei risultati recuperata con una richiesta di recupero asincrona, abbiamo interrotto alcuni pezzi dell'applicazione. Ad esempio, toccando il segno di spunta di un elemento to-do sembra non funzionare più. Durante l'aggiornamento del database, l'interfaccia utente non riflette la modifica. La soluzione è abbastanza facile da risolvere e lascerò a te l'incarico di implementare una soluzione. Ora dovresti avere una conoscenza sufficiente per comprendere il problema e trovare una soluzione adeguata.

Conclusione

Sono sicuro che sei d'accordo che il recupero asincrono è sorprendentemente facile da usare. Il sollevamento pesante viene eseguito da Core Data, il che significa che non è necessario unire manualmente i risultati della richiesta di recupero asincrona con il contesto dell'oggetto gestito. Il tuo unico compito è aggiornare l'interfaccia utente quando la richiesta di recupero asincrona ti fornisce i risultati. Insieme agli aggiornamenti batch, è una grande aggiunta al framework Core Data.

Questo articolo conclude anche questa serie su Core Data. Hai imparato molto sul framework Core Data e conosci tutti gli elementi essenziali per utilizzare Core Data in una vera applicazione. Core Data è un framework potente e, con il rilascio di iOS 8, Apple ci ha dimostrato che migliora ogni anno.