Données de base et Swift: extraction asynchrone

Dans les tranches précédentes, nous avons discuté des mises à jour et des suppressions par lots. Dans ce didacticiel, nous examinerons de plus près comment implémenter la récupération asynchrone et dans quelles situations votre application peut bénéficier de cette nouvelle API.

1. Le problème

Comme les mises à jour par lots, la récupération asynchrone fait partie de la liste de souhaits de nombreux développeurs depuis un certain temps. Les demandes de récupération peuvent être complexes et prendre un temps non négligeable. Pendant ce temps, la demande d’extraction bloque le thread sur lequel elle s’exécute et, par conséquent, bloque l’accès au contexte d’objet géré exécutant la demande d’extraction. Le problème est simple à comprendre, mais à quoi ressemble la solution d’Apple.

2. La solution

La réponse d’Apple à ce problème est l’extraction asynchrone. Une demande de récupération asynchrone s’exécute en arrière-plan. Cela signifie qu’il ne bloque pas d’autres tâches pendant son exécution, comme la mise à jour de l’interface utilisateur sur le thread principal.

La récupération asynchrone comporte également deux autres fonctionnalités pratiques, le rapport de progression et l’annulation. Une demande d’extraction asynchrone peut être annulée à tout moment, par exemple, lorsque l’utilisateur décide que la demande d’extraction prend trop de temps à se terminer. Le rapport de progression est un ajout utile pour montrer à l’utilisateur l’état actuel de la demande de récupération.

La récupération asynchrone est une API flexible. Non seulement il est possible d’annuler une demande d’extraction asynchrone, mais il est également possible d’apporter des modifications au contexte d’objet géré pendant l’exécution de la demande d’extraction asynchrone. En d’autres termes, l’utilisateur peut continuer à utiliser votre application pendant que l’application exécute une demande de récupération asynchrone en arrière-plan.

3. Comment ça marche?

À l’instar des mises à jour par lots, les demandes d’extraction asynchrones sont transmises au contexte d’objet géré en tant que NSPersistentStoreRequest object, une instance du NSAsynchronousFetchRequest classe pour être précis.

Un NSAsynchronousFetchRequest l’instance est initialisée avec un NSFetchRequest objet et un bloc de complétion. Le bloc d’achèvement est exécuté lorsque la demande d’extraction asynchrone a terminé sa demande d’extraction.

Revisitons le application à faire nous avons créé plus tôt dans cette série et remplaçons l’implémentation actuelle du NSFetchedResultsController classe avec une demande de récupération asynchrone.

Étape 1: configuration du projet

Téléchargez ou clonez le projet à partir de GitHub et ouvrez-le dans Xcode 7. Avant de pouvoir commencer à travailler avec le NSAsynchronousFetchRequest classe, nous devons apporter des modifications. Nous ne pourrons pas utiliser le NSFetchedResultsController classe pour gérer les données de la vue table depuis le NSFetchedResultsController La classe a été conçue pour fonctionner sur le thread principal.

Étape 2: remplacement du contrôleur des résultats récupérés

Commencez par mettre à jour le ViewController classe comme indiqué ci-dessous. Nous supprimons le fetchedResultsController propriété et créer une nouvelle propriété,items, de type [Item] pour stocker les choses à faire. Cela signifie également que le ViewController la classe n’a plus besoin de se conformer à la NSFetchedResultsControllerDelegate protocole.

Avant de refactoriser le viewDidLoad() méthode, je souhaite d’abord mettre à jour l’implémentation de la UITableViewDataSource protocole. Jetez un œil aux modifications que j’ai apportées.

Nous devons également modifier une ligne de code dans le prepareForSegue(_:sender:) méthode comme indiqué ci-dessous.

Enfin et surtout, supprimez la mise en œuvre du NSFetchedResultsControllerDelegate protocole puisque nous n’en avons plus besoin.

Étape 3: Création de la demande de récupération asynchrone

Comme vous pouvez le voir ci-dessous, nous créons la demande de récupération asynchrone dans le contrôleur de vue viewDidLoad() méthode. Prenons un moment pour voir ce qui se passe.

Nous commençons par créer et configurer un NSFetchRequest instance pour initialiser la demande d’extraction asynchrone. C’est cette demande d’extraction que la demande d’extraction asynchrone exécutera en arrière-plan.

Pour initialiser un NSAsynchronousFetchRequest exemple, nous invoquons init(request:completionBlock:), en passant fetchRequest et un bloc de complétion.

Le bloc d’achèvement est appelé lorsque la demande d’extraction asynchrone a terminé l’exécution de sa demande d’extraction. Le bloc de complétion prend un argument de type NSAsynchronousFetchResult, qui contient le résultat de la requête ainsi qu’une référence à la requête d’extraction asynchrone d’origine.

En relation :  Comment réinstaller Windows 10 sans perdre vos données

Dans le bloc de complétion, nous invoquons processAsynchronousFetchResult(_:), passant dans le NSAsynchronousFetchResult objet. Nous examinerons cette méthode d’aide dans quelques instants.

L’exécution de la requête de récupération asynchrone est presque identique à la façon dont nous exécutons un NSBatchUpdateRequest. Nous appelons executeRequest(_:) sur le contexte de l’objet géré, en passant la demande de récupération asynchrone.

Même si la requête de récupération asynchrone est exécutée en arrière-plan, notez que le executeRequest(_:) La méthode revient immédiatement, nous donnant un NSAsynchronousFetchResult objet. Une fois la demande de récupération asynchrone terminée, cette même NSAsynchronousFetchResult l’objet est rempli avec le résultat de la demande d’extraction.

Rappelez-vous du tutoriel précédent que executeRequest(_:) est une méthode de lancer. Nous détectons toutes les erreurs dans le catch clause de la do-catch et imprimez-les sur la console pour le débogage.

Étape 4: Traitement du résultat de l’extraction asynchrone

le processAsynchronousFetchResult(_:) n’est rien de plus qu’une méthode d’assistance dans laquelle nous traitons le résultat de la requête de récupération asynchrone. Nous définissons le contrôleur de vue items propriété avec le contenu du résultat finalResult et rechargez la vue de table.

Étape 5: Construire et exécuter

Générez le projet et exécutez l’application dans le simulateur iOS. Si votre application se bloque lorsqu’elle tente d’exécuter la requête de récupération asynchrone, vous utilisez peut-être une API obsolète à partir d’iOS 9 (et d’OS X El Capitan).

Dans Core Data et Swift: Concurrency, j’ai expliqué les différents types de concurrence qu’un contexte d’objet géré peut avoir. À partir d’iOS 9 (et d’OS X El Capitan), le ConfinementConcurrencyType est obsolète. La même chose est vraie pour le init() méthode de la NSManagedObjectContext class, car il crée une instance avec un type de concurrence ConfinementConcurrencyType.

Si votre application plante, vous utilisez probablement un contexte d’objet géré avec un ConfinementConcurrencyType type de concurrence, qui ne prend pas en charge la récupération asynchrone. Heureusement, la solution est simple. Créer un contexte d’objet géré à l’aide de l’initialiseur désigné, init(concurrencyType:), en passant MainQueueConcurrencyType ou PrivateQueueConcurrencyType comme type de concurrence.

4. Affichage des progrès

le NSAsynchronousFetchRequest La classe ajoute la prise en charge de la surveillance de la progression de la demande d’extraction et il est même possible d’annuler une demande d’extraction asynchrone, par exemple, si l’utilisateur décide qu’elle prend trop de temps à se terminer.

le NSAsynchronousFetchRequest classe exploite le NSProgress classe pour le rapport de progression ainsi que l’annulation d’une demande de récupération asynchrone. le NSProgress La classe, disponible depuis iOS 7 et OS X Mavericks, est un moyen intelligent de surveiller la progression d’une tâche sans avoir besoin de coupler étroitement la tâche à l’interface utilisateur.

le NSProgress La classe prend également en charge l’annulation, qui permet d’annuler une demande de récupération asynchrone. Découvrons ce que nous devons faire pour implémenter le rapport de progression pour la demande de récupération asynchrone.

Étape 1: Ajout de SVProgressHUD

Nous montrerons à l’utilisateur la progression de la requête de récupération asynchrone en utilisant Sam Vermettede SVProgressHUD bibliothèque. Le moyen le plus simple d’y parvenir est de CocoaPods. C’est ce que le projet Podfile ressemble à.

Courir pod install formez la ligne de commande et n’oubliez pas d’ouvrir l’espace de travail que CocoaPods a créé pour vous au lieu du projet Xcode.

Étape 2: configuration NSProgress

Dans cet article, nous n’explorerons pas la NSProgress classe plus en détail, mais n’hésitez pas à en savoir plus sur Documentation d’Apple. Nous créons un NSProgress instance dans le contrôleur de vue viewDidLoad() méthode, avant d’exécuter la requête de récupération asynchrone.

Vous serez peut-être surpris que nous définissions le nombre total d’unités sur 1. La raison est simple. Lorsque Core Data exécute la demande de récupération asynchrone, il ne sait pas combien d’enregistrements il trouvera dans le magasin persistant. Cela signifie également que nous ne pourrons pas montrer la progression relative à l’utilisateur – un pourcentage. Au lieu de cela, nous montrerons à l’utilisateur la progression absolue – le nombre d’enregistrements qu’il a trouvés.

Vous pouvez résoudre ce problème en exécutant une demande de récupération pour récupérer le nombre d’enregistrements avant d’exécuter la demande de récupération asynchrone. Cependant, je préfère ne pas le faire, car cela signifie également que la récupération des enregistrements à partir du magasin persistant prend plus de temps en raison de la demande de récupération supplémentaire au début.

En relation :  Comment suivre vos amis en temps réel à l'aide de Google Maps

Étape 3: Ajout d’un observateur

Lorsque nous exécutons la requête de récupération asynchrone, nous recevons immédiatement un NSAsynchronousFetchResult objet. Cet objet a un progress propriété, qui est de type NSProgress. C’est ça progress propriété que nous devons observer si nous voulons recevoir des mises à jour de progression.

Notez que nous appelons resignCurrent sur le progress objet pour équilibrer le plus tôt becomeCurrentWithPendingUnitCount: appel. Gardez à l’esprit que ces deux méthodes doivent être appelées sur le même thread.

Étape 4: retrait de l’observateur

Dans le bloc d’achèvement de la demande de récupération asynchrone, nous supprimons l’observateur et rejetons la palette de progression.

Avant de mettre en œuvre observeValueForKeyPath(_:ofObject:change:context:), nous devons afficher la palette de progression avant de créer la requête de récupération asynchrone.

Étape 5: Rapport d’étape

Il ne nous reste plus qu’à mettre en œuvre le observeValueForKeyPath(_:ofObject:change:context:) méthode. Nous vérifions si context est égal à ProgressContext, créer un status objet en extrayant le nombre d’enregistrements terminés du change dictionnaire et mettez à jour la palette de progression. Notez que nous mettons à jour l’interface utilisateur sur le fil principal.

5. Données factices

Si nous voulons tester correctement notre application, nous avons besoin de plus de données. Bien que je ne recommande pas d’utiliser l’approche suivante dans une application de production, c’est un moyen rapide et facile de remplir la base de données avec des données.

Ouvert AppDelegate.swift et mettre à jour le application(_:didFinishLaunchingWithOptions:) méthode comme indiqué ci-dessous. le populateDatabase() method est une méthode d’assistance simple dans laquelle nous ajoutons des données factices à la base de données.

La mise en œuvre est simple. Parce que nous ne voulons insérer des données factices qu’une seule fois, nous vérifions la base de données par défaut de l’utilisateur pour la clé "didPopulateDatabase". Si la clé n’est pas définie, nous insérons des données factices.

Le nombre d’enregistrements est important. Si vous prévoyez d’exécuter l’application sur le simulateur iOS, vous pouvez insérer 100 000 ou 1 000 000 enregistrements. Cela ne fonctionnera pas aussi bien sur un appareil physique et prendra trop de temps à terminer.

dans le for loop, nous créons un objet géré et le remplissons de données. Notez que nous ne sauvegardons pas les modifications du contexte de l’objet géré à chaque itération du for boucle.

Enfin, nous mettons à jour la base de données des paramètres par défaut de l’utilisateur pour nous assurer que la base de données n’est pas remplie au prochain lancement de l’application.

Génial. Exécutez l’application dans le simulateur iOS pour voir le résultat. Vous remarquerez que la demande de récupération asynchrone prend quelques instants pour commencer à récupérer les enregistrements et mettre à jour la palette de progression.

Affichage de la progression de la demande de récupération asynchrone

6. Changements de rupture

En remplaçant la classe de contrôleur de résultats récupérée par une demande de récupération asynchrone, nous avons cassé quelques éléments de l’application. Par exemple, appuyer sur la coche d’une tâche à faire ne semble plus fonctionner. Pendant la mise à jour de la base de données, l’interface utilisateur ne reflète pas le changement. La solution est assez simple à corriger et je vous laisse le soin de mettre en œuvre une solution. Vous devriez maintenant avoir suffisamment de connaissances pour comprendre le problème et trouver une solution appropriée.

Conclusion

Je suis sûr que vous êtes d’accord pour dire que la récupération asynchrone est étonnamment facile à utiliser. Le gros du travail est effectué par Core Data, ce qui signifie qu’il n’est pas nécessaire de fusionner manuellement les résultats de la demande de récupération asynchrone avec le contexte d’objet géré. Votre seul travail consiste à mettre à jour l’interface utilisateur lorsque la demande de récupération asynchrone vous remet les résultats.

Moyens Staff
Moyens I/O Staff vous a motivé, donner des conseils sur la technologie, le développement personnel, le style de vie et des stratégies qui vous aider.