Je travaille sur une méthode pour synchroniser les données de base stockées dans une application iPhone entre plusieurs appareils, comme un iPad ou un Mac. Il n'y a pas beaucoup (voire pas du tout) de cadres de synchronisation à utiliser avec Core Data sur iOS. Cependant, j'ai pensé au concept suivant:
- Une modification est apportée au magasin de données de base local et la modification est enregistrée. (a) Si le périphérique est en ligne, il essaie d'envoyer l'ensemble de modifications au serveur, y compris l'ID de périphérique de l'appareil qui a envoyé l'ensemble de modifications. (b) Si l'ensemble de modifications n'atteint pas le serveur, ou si l'appareil n'est pas en ligne, l'application ajoutera l'ensemble de modifications à une file d'attente à envoyer lorsqu'il sera en ligne.
- Le serveur, assis dans le cloud, fusionne les ensembles de modifications spécifiques qu'il reçoit avec sa base de données master.
- Après la fusion d'un jeu de modifications (ou d'une file d'attente de jeux de modifications) sur le serveur cloud, le serveur envoie tous ces jeux de modifications aux autres appareils enregistrés auprès du serveur à l'aide d'une sorte de système d'interrogation. (J'ai pensé à utiliser les services Push d'Apple, mais apparemment, selon les commentaires, ce n'est pas un système viable.)
Y a-t-il quelque chose d'extraordinaire auquel je dois penser? J'ai examiné les cadres REST tels que ObjectiveResource , Core Resource et RestfulCoreData . Bien sûr, tout cela fonctionne avec Ruby on Rails, auquel je ne suis pas lié, mais c'est un point de départ. Les principales exigences que j'ai pour ma solution sont:
- Toute modification doit être envoyée en arrière-plan sans interrompre le thread principal.
- Il doit utiliser le moins de bande passante possible.
J'ai réfléchi à un certain nombre de défis:
- Assurez-vous que les ID d'objet des différents magasins de données sur différents appareils sont connectés sur le serveur. C'est-à-dire que j'aurai une table d'ID d'objet et d'ID de périphérique, qui sont liés via une référence à l'objet stocké dans la base de données. J'aurai un enregistrement (DatabaseId [unique à cette table], ObjectId [unique à l'élément dans la base de données entière], Datafield1, Datafield2), le champ ObjectId référencera une autre table, AllObjects: (ObjectId, DeviceId, DeviceObjectId). Ensuite, lorsque le périphérique envoie un ensemble de modifications, il transmet l'ID de périphérique et le objectId de l'objet de données principal dans le magasin de données local. Ensuite, mon serveur cloud vérifiera par rapport à l'ID d'objet et à l'ID de périphérique dans la table AllObjects, et trouvera l'enregistrement à modifier dans la table initiale.
- Toutes les modifications doivent être horodatées afin de pouvoir être fusionnées.
- L'appareil devra interroger le serveur, sans utiliser trop de batterie.
- Les appareils locaux devront également mettre à jour tout ce qui est en mémoire si / quand des modifications sont reçues du serveur.
Y a-t-il autre chose qui me manque ici? Quels types de cadres dois-je envisager pour rendre cela possible?
Réponses:
Je suggère de lire attentivement et de mettre en œuvre la stratégie de synchronisation discutée par Dan Grover lors de la conférence iPhone 2009, disponible ici en tant que document pdf.
Il s'agit d'une solution viable et n'est pas si difficile à implémenter (Dan l'a implémentée dans plusieurs de ses applications), chevauchant la solution décrite par Chris. Pour une discussion théorique approfondie de la synchronisation, voir l'article de Russ Cox (MIT) et William Josephson (Princeton):
Synchronisation de fichiers avec des paires de temps vectorielles
qui s'applique également aux données de base avec quelques modifications évidentes. Cela fournit une stratégie de synchronisation globale beaucoup plus robuste et fiable, mais nécessite plus d'efforts pour être implémenté correctement.
ÉDITER:
Il semble que le fichier pdf de Grover ne soit plus disponible (lien brisé, mars 2015). MISE À JOUR: le lien est disponible via Way Back Machine ici
Le cadre Objective-C appelé ZSync et développé par Marcus Zarra est obsolète, étant donné qu'iCloud semble enfin prendre en charge la synchronisation correcte des données de base.
la source
J'ai fait quelque chose de similaire à ce que vous essayez de faire. Permettez-moi de vous dire ce que j'ai appris et comment je l'ai fait.
Je suppose que vous avez une relation un à un entre votre objet Core Data et le modèle (ou schéma db) sur le serveur. Vous voulez simplement synchroniser le contenu du serveur avec les clients, mais les clients peuvent également modifier et ajouter des données. Si j'ai bien compris, continuez à lire.
J'ai ajouté quatre champs pour aider à la synchronisation:
Sur le client, ajoutez du code pour définir sync_status sur 1 sur votre objet modèle chaque fois que quelque chose change et doit être synchronisé avec le serveur. Les nouveaux objets de modèle doivent générer un GUID.
La synchronisation est une seule demande. La demande contient:
Le serveur reçoit la demande et fait ceci:
L'application reçoit la réponse et fait ceci:
J'espère que ça aide. J'ai utilisé le mot enregistrement et modèle de façon interchangeable, mais je pense que vous avez compris l'idée. Bonne chance.
la source
MAX(last_modified)
, mais ce serait redondant carMAX(last_modified)
suffisant. Lesync_status
a un autre rôle. Comme je l'ai écrit plus tôt,MAX(last_modified)
détermine ce qui doit être synchronisé depuis le serveur, tout ensync_status
détermine ce qui doit être synchronisé avec le serveur.Si vous cherchez toujours un chemin à parcourir, consultez le mobile Couchbase. Cela fait tout ce que vous voulez. ( http://www.couchbase.com/nosql-databases/couchbase-mobile )
la source
Similaire comme @Cris, j'ai implémenté une classe pour la synchronisation entre le client et le serveur et résolu tous les problèmes connus jusqu'à présent (envoyer / recevoir des données vers / depuis le serveur, fusionner les conflits basés sur les horodatages, supprimer les entrées en double dans des conditions de réseau peu fiables, synchroniser les données imbriquées et fichiers etc ..)
Vous indiquez simplement à la classe quelle entité et quelles colonnes doivent être synchronisées et où se trouve votre serveur.
Vous pouvez trouver la source, un exemple de travail et plus d'instructions ici: github.com/knagode/M3Synchronization .
la source
Remarquez que l'utilisateur doit mettre à jour les données via une notification push. Utilisez un fil d'arrière-plan dans l'application pour vérifier les données locales et les données sur le serveur cloud, tandis que le changement se produit sur le serveur, modifiez les données locales, et vice versa.
Je pense donc que la partie la plus difficile est d'estimer les données de quel côté est invalidé.
J'espère que cela peut vous aider
la source
Je viens de publier la première version de ma nouvelle API Core Data Cloud Syncing, connue sous le nom de SynCloud. SynCloud a beaucoup de différences avec iCloud car il permet une interface de synchronisation multi-utilisateurs. Il est également différent des autres API de synchronisation car il permet des données relationnelles multi-tables.
Veuillez en savoir plus sur http://www.syncloudapi.com
Construit avec le SDK iOS 6, il est très à jour au 27/09/2012.
la source
Je pense qu'une bonne solution au problème du GUID est le "système d'identification distribué". Je ne sais pas quel est le terme correct, mais je pense que c'est ainsi que les documents du serveur MS SQL l'appelaient (SQL utilise / a utilisé cette méthode pour les bases de données distribuées / synchronisées). C'est assez simple:
Le serveur attribue tous les ID. Chaque fois qu'une synchronisation est effectuée, la première chose qui est vérifiée est "Combien d'ID reste-t-il sur ce client?" Si le client est faible, il demande au serveur un nouveau bloc d'ID. Le client utilise ensuite les ID de cette plage pour les nouveaux enregistrements. Cela fonctionne très bien pour la plupart des besoins, si vous pouvez attribuer un bloc suffisamment grand pour qu'il ne "jamais" s'épuise avant la prochaine synchronisation, mais pas si grand que le serveur s'épuise au fil du temps. Si le client est épuisé, la gestion peut être assez simple, dites simplement à l'utilisateur "désolé que vous ne pouvez pas ajouter plus d'éléments avant de synchroniser" ... s'il ajoute autant d'éléments, ne devrait-il pas se synchroniser pour éviter les données périmées des problèmes de toute façon?
Je pense que cela est supérieur à l'utilisation de GUID aléatoires car les GUID aléatoires ne sont pas sûrs à 100% et doivent généralement être beaucoup plus longs qu'un ID standard (128 bits contre 32 bits). Vous avez généralement des index par ID et gardez souvent des numéros d'ID en mémoire, il est donc important de les garder petits.
Je ne voulais pas vraiment poster comme réponse, mais je ne sais pas si quelqu'un verrait un commentaire, et je pense que c'est important pour ce sujet et non inclus dans les autres réponses.
la source
Vous devez d'abord repenser le nombre de données, de tables et de relations que vous aurez. Dans ma solution, j'ai implémenté la synchronisation via les fichiers Dropbox. J'observe les changements dans le MOC principal et enregistre ces données dans des fichiers (chaque ligne est enregistrée au format json gzippé). Si une connexion Internet fonctionne, je vérifie s'il y a des modifications sur Dropbox (Dropbox me donne des modifications delta), les télécharge et les fusionne (dernières victoires), et enfin place les fichiers modifiés. Avant la synchronisation, je mets le fichier de verrouillage sur Dropbox pour empêcher d'autres clients de synchroniser des données incomplètes. Lors du téléchargement des modifications, il est sûr que seules des données partielles sont téléchargées (par exemple, une connexion Internet perdue). Lorsque le téléchargement est terminé (entièrement ou partiellement), il commence à charger des fichiers dans Core Data. Lorsqu'il existe des relations non résolues (tous les fichiers ne sont pas téléchargés), il arrête le chargement des fichiers et essaie de terminer le téléchargement plus tard. Les relations sont stockées uniquement sous forme de GUID, donc je peux facilement vérifier quels fichiers à charger pour avoir une intégrité complète des données. La synchronisation démarre après que des modifications ont été apportées aux données de base. S'il n'y a pas de modifications, il vérifie les modifications sur Dropbox toutes les quelques minutes et au démarrage de l'application. De plus, lorsque des modifications sont envoyées au serveur, j'envoie une diffusion à d'autres appareils pour les informer des modifications, afin qu'ils puissent se synchroniser plus rapidement. Chaque entité synchronisée possède une propriété GUID (guid est également utilisé comme nom de fichier pour les fichiers d'échange). J'ai également une base de données Sync où je stocke la révision Dropbox de chaque fichier (je peux le comparer lorsque le delta Dropbox réinitialise son état). Les fichiers contiennent également le nom de l'entité, l'état (supprimé / non supprimé), guid (identique au nom de fichier), la révision de la base de données (pour détecter les migrations de données ou pour éviter la synchronisation avec des versions jamais d'application) et bien sûr les données (si la ligne n'est pas supprimée). donc je peux facilement vérifier quels fichiers à charger pour avoir une intégrité complète des données. La synchronisation démarre après que des modifications ont été apportées aux données de base. S'il n'y a pas de modifications, il vérifie les modifications sur Dropbox toutes les quelques minutes et au démarrage de l'application. De plus, lorsque des modifications sont envoyées au serveur, j'envoie une diffusion à d'autres appareils pour les informer des modifications, afin qu'ils puissent se synchroniser plus rapidement. Chaque entité synchronisée possède une propriété GUID (guid est également utilisé comme nom de fichier pour les fichiers d'échange). J'ai également une base de données Sync où je stocke la révision Dropbox de chaque fichier (je peux le comparer lorsque le delta Dropbox réinitialise son état). Les fichiers contiennent également le nom de l'entité, l'état (supprimé / non supprimé), guid (identique au nom de fichier), la révision de la base de données (pour détecter les migrations de données ou pour éviter la synchronisation avec des versions jamais d'application) et bien sûr les données (si la ligne n'est pas supprimée). donc je peux facilement vérifier quels fichiers à charger pour avoir une intégrité complète des données. La synchronisation démarre après que des modifications ont été apportées aux données de base. S'il n'y a pas de modifications, il vérifie les modifications sur Dropbox toutes les quelques minutes et au démarrage de l'application. De plus, lorsque des modifications sont envoyées au serveur, j'envoie une diffusion à d'autres appareils pour les informer des modifications, afin qu'ils puissent se synchroniser plus rapidement. Chaque entité synchronisée possède une propriété GUID (guid est également utilisé comme nom de fichier pour les fichiers d'échange). J'ai également une base de données Sync où je stocke la révision Dropbox de chaque fichier (je peux le comparer lorsque le delta Dropbox réinitialise son état). Les fichiers contiennent également le nom de l'entité, l'état (supprimé / non supprimé), guid (identique au nom de fichier), la révision de la base de données (pour détecter les migrations de données ou pour éviter la synchronisation avec des versions jamais d'application) et bien sûr les données (si la ligne n'est pas supprimée).
Cette solution fonctionne pour des milliers de fichiers et environ 30 entités. Au lieu de Dropbox, je pourrais utiliser le magasin de clés / valeurs comme service Web REST que je veux faire plus tard, mais je n'ai pas le temps pour cela :) Pour l'instant, à mon avis, ma solution est plus fiable qu'iCloud et, ce qui est très important, J'ai un contrôle total sur son fonctionnement (principalement parce que c'est mon propre code).
Une autre solution consiste à enregistrer les modifications MOC en tant que transactions - il y aura beaucoup moins de fichiers échangés avec le serveur, mais il est plus difficile d'effectuer le chargement initial dans le bon ordre dans des données de base vides. iCloud fonctionne de cette façon, et d'autres solutions de synchronisation ont également une approche similaire, par exemple TICoreDataSync .
-- METTRE À JOUR
Après un certain temps, j'ai migré vers Ensembles - je recommande cette solution plutôt que de réinventer la roue.
la source