Ich habe an einer Methode gearbeitet, um in einer iPhone-Anwendung gespeicherte Kerndaten zwischen mehreren Geräten wie einem iPad oder einem Mac zu synchronisieren. Es gibt nicht viele (wenn überhaupt) Synchronisierungsframeworks für die Verwendung mit Core Data unter iOS. Ich habe jedoch über das folgende Konzept nachgedacht:
- Der lokale Kerndatenspeicher wird geändert, und die Änderung wird gespeichert. (a) Wenn das Gerät online ist, versucht es, das Änderungsset an den Server zu senden, einschließlich der Geräte-ID des Geräts, das das Änderungsset gesendet hat. (b) Wenn der Änderungssatz den Server nicht erreicht oder wenn das Gerät nicht online ist, fügt die App den Änderungssatz einer Warteschlange hinzu, die gesendet werden soll, wenn er online geschaltet wird.
- Der Server in der Cloud führt die spezifischen Änderungssätze, die er empfängt, mit seiner Masterdatenbank zusammen.
- Nachdem ein Änderungssatz (oder eine Warteschlange mit Änderungssätzen) auf dem Cloud-Server zusammengeführt wurde, überträgt der Server alle diese Änderungssätze mithilfe eines Abfragesystems auf die anderen beim Server registrierten Geräte. (Ich dachte, ich würde Apples Push-Dienste nutzen, aber laut den Kommentaren ist dies anscheinend kein funktionsfähiges System.)
Gibt es etwas Besonderes, über das ich nachdenken muss? Ich habe mir REST-Frameworks wie ObjectiveResource , Core Resource und RestfulCoreData angesehen . Natürlich arbeiten alle mit Ruby on Rails zusammen, an das ich nicht gebunden bin, aber es ist ein Anfang. Die Hauptanforderungen, die ich an meine Lösung habe, sind:
- Alle Änderungen sollten im Hintergrund gesendet werden, ohne den Hauptthread anzuhalten.
- Es sollte so wenig Bandbreite wie möglich verbraucht werden.
Ich habe über eine Reihe von Herausforderungen nachgedacht:
- Stellen Sie sicher, dass die Objekt-IDs für die verschiedenen Datenspeicher auf verschiedenen Geräten auf dem Server angehängt sind. Das heißt, ich werde eine Tabelle mit Objekt-IDs und Geräte-IDs haben, die über einen Verweis auf das in der Datenbank gespeicherte Objekt verknüpft sind. Ich werde einen Datensatz haben (DatabaseId [eindeutig für diese Tabelle], ObjectId [eindeutig für das Element in der gesamten Datenbank], Datafield1, Datafield2), das ObjectId-Feld verweist auf eine andere Tabelle, AllObjects: (ObjectId, DeviceId, DeviceObjectId). Wenn das Gerät dann einen Änderungssatz hochschiebt, leitet es die Geräte-ID und die Objekt-ID vom Kerndatenobjekt im lokalen Datenspeicher weiter. Dann prüft mein Cloud-Server die Objekt-ID und die Geräte-ID in der AllObjects-Tabelle und findet den zu ändernden Datensatz in der Anfangstabelle.
- Alle Änderungen sollten mit einem Zeitstempel versehen sein, damit sie zusammengeführt werden können.
- Das Gerät muss den Server abfragen, ohne zu viel Batterie zu verbrauchen.
- Die lokalen Geräte müssen auch alle im Speicher gespeicherten Daten aktualisieren, wenn Änderungen vom Server empfangen werden.
Fehlt mir hier noch etwas? Welche Arten von Frameworks sollte ich prüfen, um dies zu ermöglichen?
Antworten:
Ich empfehle, die von Dan Grover auf der iPhone 2009-Konferenz diskutierte Synchronisierungsstrategie sorgfältig zu lesen und umzusetzen, die hier als PDF-Dokument verfügbar ist .
Dies ist eine praktikable Lösung und nicht so schwer zu implementieren (Dan hat dies in mehreren seiner Anwendungen implementiert), da sie sich mit der von Chris beschriebenen Lösung überschneidet. Für eine eingehende theoretische Diskussion der Synchronisierung siehe das Papier von Russ Cox (MIT) und William Josephson (Princeton):
Dateisynchronisation mit Vektorzeitpaaren
Dies gilt auch für Kerndaten mit einigen offensichtlichen Änderungen. Dies bietet eine insgesamt viel robustere und zuverlässigere Synchronisierungsstrategie, erfordert jedoch mehr Aufwand, um korrekt implementiert zu werden.
BEARBEITEN:
Es scheint, dass die PDF-Datei des Grovers nicht mehr verfügbar ist (defekter Link, März 2015). UPDATE: Der Link ist über die Way Back Machine hier verfügbar
Das von Marcus Zarra entwickelte Objective-C-Framework namens ZSync ist veraltet, da iCloud endlich die korrekte Synchronisierung der Kerndaten zu unterstützen scheint.
quelle
Ich habe etwas Ähnliches getan, wie Sie es versuchen. Lassen Sie mich Ihnen sagen, was ich gelernt habe und wie ich es getan habe.
Ich gehe davon aus, dass Sie eine Eins-zu-Eins-Beziehung zwischen Ihrem Core Data-Objekt und dem Modell (oder Datenbankschema) auf dem Server haben. Sie möchten lediglich den Serverinhalt mit den Clients synchron halten, Clients können jedoch auch Daten ändern und hinzufügen. Wenn ich das richtig verstanden habe, dann lies weiter.
Ich habe vier Felder hinzugefügt, um die Synchronisation zu unterstützen:
Fügen Sie auf dem Client Code hinzu, um sync_status für Ihr Modellobjekt auf 1 zu setzen, wenn sich etwas ändert und mit dem Server synchronisiert werden muss. Neue Modellobjekte müssen eine GUID generieren.
Die Synchronisation ist eine einzelne Anforderung. Die Anfrage enthält:
Der Server erhält die Anfrage und führt Folgendes aus:
Die App erhält die Antwort und führt Folgendes aus:
Ich hoffe das hilft. Ich habe das Wort Datensatz und Modell austauschbar verwendet, aber ich denke, Sie haben die Idee. Viel Glück.
quelle
MAX(last_modified)
, aber das wäre redundant, da diesMAX(last_modified)
ausreicht. Dassync_status
hat eine andere Rolle. Wie ich bereits geschrieben habe, wirdMAX(last_modified)
festgelegt, was vom Server synchronisiert werden muss, währendsync_status
festgelegt wird, was mit dem Server synchronisiert werden muss.Wenn Sie noch nach einem Weg suchen, schauen Sie sich das Couchbase-Handy an. Dies macht im Grunde alles, was Sie wollen. ( http://www.couchbase.com/nosql-databases/couchbase-mobile )
quelle
Ähnlich wie bei @Cris habe ich eine Klasse für die Synchronisation zwischen Client und Server implementiert und alle bisher bekannten Probleme gelöst (Daten zum / vom Server senden / empfangen, Konflikte basierend auf Zeitstempeln zusammenführen, doppelte Einträge unter unzuverlässigen Netzwerkbedingungen entfernen, verschachtelte Daten synchronisieren und Dateien etc ..)
Sie teilen der Klasse einfach mit, welche Entität und welche Spalten synchronisiert werden sollen und wo sich Ihr Server befindet.
Quelle, Arbeitsbeispiel und weitere Anweisungen finden Sie hier: github.com/knagode/M3Synchronization .
quelle
Beachten Sie, dass der Benutzer die Daten per Push-Benachrichtigung aktualisieren muss. Verwenden Sie einen Hintergrund-Thread in der App, um die lokalen Daten und die Daten auf dem Cloud-Server zu überprüfen. Während Änderungen auf dem Server vorgenommen werden, ändern Sie die lokalen Daten und umgekehrt.
Ich denke, der schwierigste Teil ist es, Daten zu schätzen, bei denen die Seite ungültig ist.
Hoffe das kann dir helfen
quelle
Ich habe gerade die erste Version meiner neuen Core Data Cloud Syncing-API veröffentlicht, die als SynCloud bekannt ist. SynCloud weist viele Unterschiede zu iCloud auf, da es eine Mehrbenutzer-Synchronisierungsschnittstelle ermöglicht. Es unterscheidet sich auch von anderen Synchronisierungs-APIs, da es relationale Daten mit mehreren Tabellen ermöglicht.
Weitere Informationen finden Sie unter http://www.syncloudapi.com
Mit iOS 6 SDK erstellt, ist es ab dem 27.09.2012 sehr aktuell.
quelle
Ich denke, eine gute Lösung für das GUID-Problem ist das "verteilte ID-System". Ich bin mir nicht sicher, wie der richtige Begriff lautet, aber ich denke, dass MS SQL Server-Dokumente ihn so aufrufen (SQL verwendet diese Methode für verteilte / synchronisierte Datenbanken). Es ist ziemlich einfach:
Der Server weist alle IDs zu. Jedes Mal, wenn eine Synchronisierung durchgeführt wird, wird zunächst überprüft, wie viele IDs noch auf diesem Client vorhanden sind. Wenn der Client zur Neige geht, fragt er den Server nach einem neuen ID-Block. Der Client verwendet dann IDs in diesem Bereich für neue Datensätze. Dies funktioniert für die meisten Anforderungen hervorragend, wenn Sie einen Block zuweisen können, der groß genug ist, dass er vor der nächsten Synchronisierung "nie" ausgeht, aber nicht so groß, dass der Server im Laufe der Zeit ausgeht. Wenn der Client jemals leer ist, kann die Handhabung recht einfach sein. Sagen Sie dem Benutzer einfach "Entschuldigung, dass Sie bis zur Synchronisierung keine weiteren Elemente hinzufügen können". Wenn er so viele Elemente hinzufügt, sollten sie nicht synchronisiert werden, um veraltete Daten zu vermeiden Probleme trotzdem?
Ich denke, dies ist der Verwendung von zufälligen GUIDs überlegen, da zufällige GUIDs nicht 100% sicher sind und normalerweise viel länger als eine Standard-ID sein müssen (128 Bit gegenüber 32 Bit). Normalerweise haben Sie Indizes nach ID und behalten häufig ID-Nummern im Speicher. Daher ist es wichtig, diese klein zu halten.
Ich wollte nicht wirklich als Antwort posten, aber ich weiß nicht, dass jemand dies als Kommentar sehen würde, und ich denke, dass es für dieses Thema wichtig ist und nicht in anderen Antworten enthalten ist.
quelle
Zuerst sollten Sie überdenken, wie viele Daten, Tabellen und Beziehungen Sie haben werden. In meiner Lösung habe ich die Synchronisierung über Dropbox-Dateien implementiert. Ich beobachte Änderungen im Haupt-MOC und speichere diese Daten in Dateien (jede Zeile wird als gzipped json gespeichert). Wenn eine Internetverbindung funktioniert, überprüfe ich, ob Änderungen an Dropbox vorgenommen wurden (Dropbox gibt mir Delta-Änderungen), lade sie herunter und füge sie zusammen (letzte Gewinne) und lege schließlich geänderte Dateien ab. Vor der Synchronisierung habe ich die Sperrdatei in Dropbox abgelegt, um zu verhindern, dass andere Clients unvollständige Daten synchronisieren. Beim Herunterladen von Änderungen ist sicher, dass nur Teildaten heruntergeladen werden (z. B. unterbrochene Internetverbindung). Wenn der Download (vollständig oder teilweise) abgeschlossen ist, werden Dateien in Core Data geladen. Wenn ungelöste Beziehungen bestehen (nicht alle Dateien werden heruntergeladen), werden die Dateien nicht mehr geladen und der Download wird später beendet. Beziehungen werden nur als GUID gespeichert, sodass ich leicht überprüfen kann, welche Dateien geladen werden müssen, um die vollständige Datenintegrität zu gewährleisten. Die Synchronisierung beginnt, nachdem Änderungen an den Kerndaten vorgenommen wurden. Wenn es keine Änderungen gibt, wird alle paar Minuten und beim Start der App nach Änderungen in Dropbox gesucht. Wenn Änderungen an den Server gesendet werden, sende ich zusätzlich eine Übertragung an andere Geräte, um sie über Änderungen zu informieren, damit sie schneller synchronisiert werden können. Jede synchronisierte Entität verfügt über eine GUID-Eigenschaft (guid wird auch als Dateiname für Austauschdateien verwendet). Ich habe auch eine Synchronisierungsdatenbank, in der ich die Dropbox-Revision jeder Datei speichere (ich kann sie vergleichen, wenn das Dropbox-Delta den Status zurücksetzt). Dateien enthalten auch den Namen der Entität, den Status (gelöscht / nicht gelöscht), die Guid (wie der Dateiname), die Datenbankrevision (um Datenmigrationen zu erkennen oder die Synchronisierung mit nie App-Versionen zu vermeiden) und natürlich die Daten (wenn die Zeile nicht gelöscht wird). So kann ich einfach überprüfen, welche Dateien geladen werden müssen, um die vollständige Datenintegrität zu gewährleisten. Die Synchronisierung beginnt, nachdem Änderungen an den Kerndaten vorgenommen wurden. Wenn es keine Änderungen gibt, wird alle paar Minuten und beim Start der App nach Änderungen in Dropbox gesucht. Wenn Änderungen an den Server gesendet werden, sende ich zusätzlich eine Übertragung an andere Geräte, um sie über Änderungen zu informieren, damit sie schneller synchronisiert werden können. Jede synchronisierte Entität verfügt über eine GUID-Eigenschaft (guid wird auch als Dateiname für Austauschdateien verwendet). Ich habe auch eine Synchronisierungsdatenbank, in der ich die Dropbox-Revision jeder Datei speichere (ich kann sie vergleichen, wenn das Dropbox-Delta den Status zurücksetzt). Dateien enthalten auch den Namen der Entität, den Status (gelöscht / nicht gelöscht), die Guid (wie der Dateiname), die Datenbankrevision (um Datenmigrationen zu erkennen oder die Synchronisierung mit nie App-Versionen zu vermeiden) und natürlich die Daten (wenn die Zeile nicht gelöscht wird). So kann ich einfach überprüfen, welche Dateien geladen werden müssen, um die vollständige Datenintegrität zu gewährleisten. Die Synchronisierung beginnt, nachdem Änderungen an den Kerndaten vorgenommen wurden. Wenn es keine Änderungen gibt, wird alle paar Minuten und beim Start der App nach Änderungen in Dropbox gesucht. Wenn Änderungen an den Server gesendet werden, sende ich zusätzlich eine Übertragung an andere Geräte, um sie über Änderungen zu informieren, damit sie schneller synchronisiert werden können. Jede synchronisierte Entität verfügt über eine GUID-Eigenschaft (guid wird auch als Dateiname für Austauschdateien verwendet). Ich habe auch eine Synchronisierungsdatenbank, in der ich die Dropbox-Revision jeder Datei speichere (ich kann sie vergleichen, wenn das Dropbox-Delta den Status zurücksetzt). Dateien enthalten auch den Namen der Entität, den Status (gelöscht / nicht gelöscht), die Guid (wie der Dateiname), die Datenbankrevision (um Datenmigrationen zu erkennen oder die Synchronisierung mit nie App-Versionen zu vermeiden) und natürlich die Daten (wenn die Zeile nicht gelöscht wird).
Diese Lösung funktioniert für Tausende von Dateien und etwa 30 Entitäten. Anstelle von Dropbox könnte ich den Schlüssel- / Wertspeicher als REST-Webdienst verwenden, den ich später ausführen möchte, aber dafür keine Zeit habe :) Derzeit ist meine Lösung meiner Meinung nach zuverlässiger als iCloud und, was sehr wichtig ist, Ich habe die volle Kontrolle darüber, wie es funktioniert (hauptsächlich, weil es mein eigener Code ist).
Eine andere Lösung besteht darin, MOC-Änderungen als Transaktionen zu speichern. Es werden viel weniger Dateien mit dem Server ausgetauscht, aber es ist schwieriger, das anfängliche Laden in der richtigen Reihenfolge in leere Kerndaten durchzuführen. iCloud funktioniert auf diese Weise, und auch andere Synchronisierungslösungen verfolgen einen ähnlichen Ansatz, z . B. TICoreDataSync .
- UPDATE
Nach einer Weile bin ich zu Ensembles migriert - ich empfehle diese Lösung, um das Rad neu zu erfinden.
quelle