1
votes

Comment créer un instantané de l'état CoreData?

Histoire de fond

Je développe une grosse application iOS. Cette application fonctionne sous des hypothèses spécifiques. Le principal d'entre eux est que l'application doit fonctionner hors ligne avec le stockage interne qui est un instantané du dernier état synchronisé des données enregistrées sur le serveur. J'ai décidé d'utiliser CoreData pour gérer ce stockage. Chaque fois que l'application est lancée, je vérifie si la connexion WiFi est activée, puis j'essaye de synchroniser le stockage avec le serveur. La synchronisation peut prendre environ 3 minutes en raison de la taille des données.

Le processus de synchronisation se compose de plusieurs étapes et dans chacune d'elles je:

  • récupérer des données sur le serveur (XML)
  • désérialiser
  • enregistrez-le dans Core Data

Problème

Le processus de synchronisation peut être interrompu pour plusieurs raisons (connexion Internet, serveur en panne, utilisateur quittant l'application, etc.). Cela peut entraîner une désynchronisation des données.

Supposons que le processus de synchronisation comporte 5 étapes et qu'il s'arrête après la troisième. Il en résulte que 3/5 des données sont mises à jour dans la mémoire interne et le reste n'est pas synchronisé. Je ne peux pas l'autoriser car les données sont étroitement liées les unes aux autres (logique métier).

Objectif

Je ne sais pas si c'est possible mais je pense mettre en œuvre une solution. Au début du processus de synchronisation, je voudrais créer un instantané (une sorte de copie) de l'état actuel de Core Date et pendant le processus de synchronisation, travailler dessus. Lorsque le processus de synchronisation se termine avec succès, cet instantané peut remplacer l'état actuel de CoreData. Lorsque la synchronisation s'interrompt, l'instantané peut être simplement abandonné. Ma mémoire interne sera sécurisée.

Questions

  • Comment créer un instantané CoreData?
  • Comment travailler avec un instantané CoreData?
  • Comment remplacer l'état CoreDate par un instantané?

Merci de vos conseils pour toute aide. Des exemples de code, si c'est possible, seront appréciés.

MODIFIER 1

La taille des données est trop grande pour les gérer avec plusieurs contextes CoreData. Pendant la synchronisation, j'enregistre le contexte actuel plusieurs fois pour nettoyer la mémoire. Si je ne le fais pas, l'application plantera avec une erreur de mémoire.

Je pense que cela devrait être résolu avec plusieurs NSPersistentStoreCoordinator en utilisant par exemple cette méthode: lien . Malheureusement, je ne sais pas comment mettre en œuvre cela.


6 commentaires

oleb.net/blog/2018/03/core-data-sqlite -sauvegarde


Merci @canister_exister mais cela ne couvre qu'une partie de mon problème. :)


Pouvez-vous créer un contexte d'objet géré pour votre synchronisation? Vous pouvez valider les modifications si tout se déroule correctement, ou appeler rollback () si ce n'est pas le cas?


@AaronBrager Merci pour la réponse. J'ai édité ma question.


Pensez-vous que cette question / réponse pourrait être utile? stackoverflow .com / questions / 50647579 /… Vous pouvez sauvegarder votre boutique actuelle, en créer une nouvelle, et si la synchronisation à partir du serveur échoue, vous pouvez revenir à l'ancienne en utilisant le replacePersistentStore (... ) que vous avez décrite.


Donc, si 99% des données ont été téléchargées et traitées lorsqu'une interruption de quelque sorte se produit, vous voulez jeter ces 99% et recommencer la prochaine fois?


3 Réponses :


0
votes

Vous devez faire exactement ce que vous avez dit. Créez simplement une classe (appelons-la SyncBuffer) avec les méthodes "load", "sync" et "save".

La méthode "load" doit lire toutes les entités de CoreData et les stocker dans des variables de classe. La méthode "sync" devrait effectuer toute la synchronisation en utilisant des variables de classe. Enfin, la méthode "save" devrait enregistrer toutes les valeurs des variables de classe dans CoreData - ici, vous pouvez même supprimer toutes les données de CoreData et enregistrer de nouvelles valeurs de SyncBuffer.


3 commentaires

Merci pour la réponse. J'ai édité ma question. Malheureusement, garder les données en mémoire plantera l'application.


Cette synchronisation fonctionne de deux manières ou vous ne faites que synchroniser les données du serveur vers votre application? Pendant la synchronisation, comparez-vous des objets entiers ou juste quelques identifiants? Ce sera vraiment bien d'avoir plus d'informations sur votre application.


Une manière. Serveur -> App. La synchronisation remplace les modèles avec le même ID.



0
votes

Une pile CoreData est composée en son cœur de trois composants: un contexte (NSManagedObjectContext) un modèle (NSManagedObjectModel) et le coordinateur de magasin (NSPersistentStoreCoordinator / NSPersistentStore).

Ce que vous voulez, c'est avoir deux contextes différents , qui partagent le même modèle mais utilisent deux magasins différents . Le magasin lui-même sera du même type (c'est-à-dire une base de données SQLite) mais utilisera un fichier source différent.

Sur cette page, vous pouvez voir de la documentation sur la pile:

https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/InitializingtheCoreDataStack.html#//apple_ref/doc/uid/TP40001075-CH4-SW1

Le NSPersistentContainer est une classe pratique pour initialiser la pile CoreData.

Prenons l'exemple de l'initialisation d'un NSPersistentContainer à partir du lien: vous pouvez avoir exactement le même code pour l'initialiser, deux fois , mais à la seule différence que les deux NSPersistentContainer utilisent un un fichier .sqlite différent : c'est-à-dire que vous pouvez avoir deux propriétés dans votre délégué d'application appelées managedObjectContextForUI et managedObjectContextForSyncing qui charge différents fichiers .sqlite. Ensuite, dans votre programme, vous pouvez utiliser le contexte d'un magasin pour obtenir les données actuelles à montrer à l'utilisateur et vous pouvez utiliser le contexte qui utilise l'autre magasin avec un autre .sqlite si vous effectuez des opérations de synchronisation . Lorsque les opérations de synchronisation sont enfin terminées, vous pouvez éventuellement échanger les deux fichiers et après avoir effacé et rechargé le NSPersistentContainer (cela peut être délicat, car vous voudrez invalider et recharger tous les objets gérés: vous passez à un tout nouveau contexte), vous pouvez puis montrez les données nouvellement synchronisées à l'utilisateur et recommencez la synchronisation sur un nouveau fichier .sqlite .


0 commentaires

0
votes

La façon dont je comprends le problème est que vous souhaitez pouvoir télécharger un grand "graphe d'objets". Il est cependant si volumineux qu'il ne peut pas être chargé en même temps en mémoire, vous devrez donc le diviser en morceaux, puis le fusionner localement dans les données Core.

Si tel est le cas, je pense que ce n’est pas anodin. Je ne suis pas sûr de pouvoir penser à une solution directe sans comprendre les relations d'objet et même dans ce cas, cela peut être vraiment écrasant.

Une solution trop simpliste peut être de générer le fichier sqlite sur le backend puis de le télécharger en morceaux; cela semble moche, mais cela sert à séparer la logique métier de la synchronisation, c'est-à-dire que le fichier sqlite devient la couche de transport. Donc, je pense que l'essence de la solution serait de trouver un moyen de représenter physiquement les données que vous synchronisez dans un format qui permet de les diviser en morceaux et qui peut ensuite être fusionné dans un fichier sqlite (si vous insistez pour utiliser Core données).

Veuillez également noter qu'à ma connaissance, Amazon ( https://aws.amazon.com/appsync / ) et Realm ( https://realm.io/blog/ introduction-realm-mobile-platform / ) fournit une synchronisation en arrière-plan de votre base de données locale, mais ce sont des services payants et vous devrez faire attention à ne pas être verrouillé (ne devrait pas dépendre de leurs bibliothèques dans votre couche de modèle, au lieu de cela avoir une couche de traduction).


0 commentaires