it-swarm.com.de

Wie synchronisiere ich iPhone Core-Daten mit dem Webserver und schiebe sie dann auf andere Geräte?

Ich habe an einer Methode gearbeitet, mit der in einer iPhone-Anwendung gespeicherte Kerndaten zwischen mehreren Geräten, z. B. einem iPad oder einem Mac, synchronisiert werden können. Es gibt nicht viele (wenn überhaupt) Synchronisierungs-Frameworks für die Verwendung mit Core Data unter iOS. Ich habe jedoch über folgendes Konzept nachgedacht:

  1. Am lokalen Kerndatenspeicher wird eine Änderung vorgenommen und die Änderung wird gespeichert. (a) Wenn das Gerät online ist, versucht es, den Änderungssatz einschließlich der Geräte-ID des Geräts, das den Änderungssatz gesendet hat, an den Server zu senden. (b) Wenn der Änderungssatz den Server nicht erreicht oder das Gerät nicht online ist, fügt die App den Änderungssatz zu einer Warteschlange hinzu, die gesendet werden soll, wenn es online geschaltet wird.
  2. Der in der Cloud befindliche Server führt die spezifischen Änderungssätze, die er erhält, mit seiner master-Datenbank zusammen.
  3. Nachdem ein Änderungssatz (oder eine Warteschlange von Änderungssätzen) auf dem Cloud-Server zusammengeführt wurde, überträgt der Server alle diese Änderungssätze mithilfe eines Abfragesystems auf die anderen auf dem Server registrierten Geräte. (Ich dachte, ich würde Apples Push-Dienste nutzen, aber den Kommentaren zufolge 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, an das ich nicht gebunden bin, aber es ist ein Anfang. Die Hauptanforderungen, die ich an meine Lösung habe, sind:

  1. Alle Änderungen sollten im Hintergrund gesendet werden, ohne den Hauptthread anzuhalten.
  2. Es sollte so wenig Bandbreite wie möglich verbraucht werden.

Ich habe über eine Reihe von Herausforderungen nachgedacht:

  1. Stellen Sie sicher, dass die Objekt-IDs für die verschiedenen Datenspeicher auf verschiedenen Geräten an den 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 habe einen Datensatz (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 einen Änderungssatz hochlädt, werden die Geräte-ID und die Objekt-ID vom Kerndatenobjekt im lokalen Datenspeicher weitergeleitet. Dann vergleicht mein Cloud-Server die Objekt-ID und die Geräte-ID in der AllObjects-Tabelle und sucht den zu ändernden Datensatz in der Anfangstabelle.
  2. Alle Änderungen sollten mit einem Zeitstempel versehen werden, damit sie zusammengeführt werden können.
  3. Das Gerät muss den Server abfragen, ohne zu viel Batterie zu verbrauchen.
  4. Die lokalen Geräte müssen auch alle im Speicher befindlichen Daten aktualisieren, wenn Änderungen vom Server empfangen werden.

Fehlt hier noch etwas? Auf welche Frameworks sollte ich achten, um dies zu ermöglichen?

291
Jason

Ich empfehle, die von Dan Grover auf der iPhone 2009-Konferenz diskutierte Synchronisierungsstrategie sorgfältig zu lesen und umzusetzen. Diese kann hier als PDF-Dokument heruntergeladen werden.

Dies ist eine praktikable Lösung und nicht so schwer zu implementieren (Dan implementierte dies in mehreren seiner Anwendungen), was sich mit der von Chris beschriebenen Lösung überschneidet. Eine eingehende theoretische Diskussion der Synchronisation finden Sie in dem Artikel von Russ Cox (MIT) und William Josephson (Princeton):

Dateisynchronisation mit Vektorzeitpaaren

dies gilt auch für Kerndaten mit einigen offensichtlichen Änderungen. Dies bietet insgesamt eine wesentlich 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 verfügbar hier

Das von Marcus Zarra entwickelte Objective-C-Framework ZSync ist veraltet, da iCloud anscheinend die korrekte Synchronisation der Kerndaten unterstützt.

141
Massimo Cafaro

Ich habe etwas Ähnliches getan wie Sie. Lassen Sie mich Ihnen sagen, was ich gelernt habe und wie ich es gemacht habe.

Ich gehe davon aus, dass Sie eine Eins-zu-Eins-Beziehung zwischen Ihrem Core Data-Objekt und dem Modell (oder DB-Schema) auf dem Server haben. Sie möchten lediglich den Serverinhalt mit den Clients synchronisieren, 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 Synchronisierung zu unterstützen:

  1. sync_status - Fügen Sie dieses Feld nur Ihrem Kerndatenmodell hinzu. Es wird von der App verwendet, um festzustellen, ob für das Element eine Änderung aussteht. Ich verwende die folgenden Codes: 0 bedeutet, dass keine Änderungen vorgenommen wurden, 1 bedeutet, dass es sich in der Warteschlange befindet, um mit dem Server synchronisiert zu werden, und 2 bedeutet, dass es sich um ein temporäres Objekt handelt, das gelöscht werden kann.
  2. is_deleted - Fügen Sie dies dem Server und dem Kerndatenmodell hinzu. Ereignis löschen sollte eigentlich keine Zeile aus der Datenbank oder Ihrem Client-Modell löschen, da Sie nichts zurücksynchronisieren müssen. Mit diesem einfachen Booleschen Flag können Sie is_deleted auf 1 setzen, es synchronisieren und alle sind glücklich. Sie müssen auch den Code auf dem Server und dem Client ändern, um nicht gelöschte Elemente mit "is_deleted = 0" abzufragen.
  3. last_modified - Fügen Sie dies dem Server und dem Kerndatenmodell hinzu. Dieses Feld sollte vom Server automatisch mit dem aktuellen Datum und der aktuellen Uhrzeit aktualisiert werden, wenn sich in diesem Datensatz etwas ändert. Es sollte niemals vom Client geändert werden.
  4. guid - Fügen Sie eine global eindeutige ID hinzu (siehe http: //en.wikipedia.org/wiki/Globally_unique_identifier ) Feld an den Server und Core-Datenmodell. Dieses Feld wird zum Primärschlüssel und ist wichtig, wenn Sie neue Datensätze auf dem Client erstellen. Normalerweise ist Ihr Primärschlüssel eine inkrementelle Ganzzahl auf dem Server. Wir müssen jedoch berücksichtigen, dass Inhalte offline erstellt und später synchronisiert werden können. Mit der GUID können wir einen Schlüssel erstellen, während wir offline sind.

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 Synchronisierung ist eine einzelne Anforderung. Die Anfrage enthält:

  • Der MAX last_modified Zeitstempel Ihrer Modellobjekte. Dies teilt dem Server mit, dass Sie Änderungen erst nach diesem Zeitstempel wünschen.
  • Ein JSON-Array, das alle Elemente mit sync_status = 1 enthält.

Der Server erhält die Anfrage und führt dies aus:

  • Es übernimmt den Inhalt aus dem JSON-Array und ändert oder fügt die darin enthaltenen Datensätze hinzu. Das Feld last_modified wird automatisch aktualisiert.
  • Der Server gibt ein JSON-Array zurück, das alle Objekte enthält, deren Zeitstempel last_modified größer ist als der in der Anforderung gesendete Zeitstempel. Dies schließt die gerade empfangenen Objekte ein, die als Bestätigung dafür dienen, dass der Datensatz erfolgreich mit dem Server synchronisiert wurde.

Die App erhält die Antwort und führt dies aus:

  • Es übernimmt den Inhalt aus dem JSON-Array und ändert oder fügt die darin enthaltenen Datensätze hinzu. Jeder Datensatz erhält den sync_status 0.

Ich hoffe das hilft. Ich habe den Word-Datensatz und das Modell austauschbar verwendet, aber ich glaube, Sie haben die Idee. Viel Glück.

270
chris

Wenn Sie noch auf der Suche nach einer Lösung sind, schauen Sie sich das Couchbase-Handy an. Das macht im Grunde alles, was Sie wollen. ( http://www.couchbase.com/nosql-databases/couchbase-mobile )

11
radiospiel

Ähnlich wie @Cris habe ich class für die Synchronisation zwischen Client und Server implementiert und alle bisher bekannten Probleme behoben (Senden/Empfangen von Daten zum/vom Server, Zusammenführen von Konflikten basierend auf Zeitstempeln, Entfernen doppelter Einträge unter unzuverlässigen Netzwerkbedingungen, Synchronisieren verschachtelter Daten und Dateien etc ..)

Sie teilen der Klasse einfach mit, welche Entität und welche Spalten synchronisiert werden sollen und wo sich Ihr Server befindet.

M3Synchronization * syncEntity = [[M3Synchronization alloc] initForClass: @"Car"
                                                              andContext: context
                                                            andServerUrl: kWebsiteUrl
                                             andServerReceiverScriptName: kServerReceiverScript
                                              andServerFetcherScriptName: kServerFetcherScript
                                                    ansSyncedTableFields:@[@"licenceNumber", @"manufacturer", @"model"]
                                                    andUniqueTableFields:@[@"licenceNumber"]];


syncEntity.delegate = self; // delegate should implement onComplete and onError methods
syncEntity.additionalPostParamsDictionary = ... // add some POST params to authenticate current user

[syncEntity sync];

Quelle, Arbeitsbeispiel und weitere Anweisungen finden Sie hier: github.com/knagode/M3Synchronization .

7
knagode

Ich denke, eine gute Lösung für das Problem GUID ist "verteiltes ID-System". Ich bin mir nicht sicher, wie der richtige Begriff lautet, aber ich denke, das ist, was MS SQL Server-Dokumente verwendet haben, um es aufzurufen ( SQL verwendet/verwendet diese Methode für verteilte/synchronisierte Datenbanken.

Der Server weist alle IDs zu. Bei jeder Synchronisierung wird als Erstes die Option "Wie viele IDs habe ich noch auf diesem Client?" Aktiviert. 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ß ist, dass der Server mit der Zeit ausgeht. Sollte der Client einmal zur Neige gehen, kann die Handhabung recht einfach sein. Sagen Sie dem Benutzer einfach "Entschuldigung, Sie können nicht mehr Elemente hinzufügen, bis Sie synchronisieren" 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 verfügen Sie über Indizes nach ID und behalten ID-Nummern häufig im Speicher. Daher ist es wichtig, diese klein zu halten.

Wollte eigentlich nicht als Antwort posten, aber ich weiß nicht, dass irgendjemand einen Kommentar sehen würde, und ich denke, es ist wichtig für dieses Thema und nicht in anderen Antworten enthalten.

5
eselk

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-Synchronisierungsoberfläche ermöglicht. Es unterscheidet sich auch von anderen Synchronisierungs-APIs, da es relationale Mehrtabellendaten ermöglicht.

Weitere Informationen finden Sie unter http://www.syncloudapi.com

Mit iOS 6 SDK erstellt, ist es ab dem 27.09.2012 auf dem neuesten Stand.

5
logan

Benachrichtigen Sie den Benutzer, die Daten per Push-Benachrichtigung zu aktualisieren. Verwenden Sie einen Hintergrund-Thread in der App, um die lokalen Daten und die Daten auf dem Cloud-Server zu überprüfen. Während die Änderung auf dem Server stattfindet, ändern Sie die lokalen Daten und umgekehrt.

Ich denke, der schwierigste Teil ist es, abzuschätzen, auf welcher Seite Daten ungültig sind.

Hoffe das kann dir helfen

5
Stan

Zuerst sollten Sie überlegen, 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 besteht, überprüfe ich, ob Änderungen an Dropbox vorgenommen wurden (Dropbox gibt mir Delta-Änderungen), lade sie herunter und füge sie zusammen (die letzten Siege) und lege schließlich geänderte Dateien ab. Vor der Synchronisierung sperre ich Dropbox, um zu verhindern, dass andere Clients unvollständige Daten synchronisieren. Beim Herunterladen von Änderungen ist es sicher, dass nur teilweise Daten heruntergeladen werden (z. B. unterbrochene Internetverbindung). Wenn der Download vollständig oder teilweise abgeschlossen ist, werden die 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 zu einem späteren Zeitpunkt beendet. Beziehungen werden nur als GUID gespeichert, sodass ich problemlos ü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. Zusätzlich, wenn Änderungen an den Server gesendet werden, sende ich eine Übertragung an andere Geräte, um sie über Änderungen zu informieren, damit sie schneller synchronisiert werden können. Jede synchronisierte Entität hat die Eigenschaft GUID (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 Dropbox Delta zurückgesetzt wird) Die Dateien enthalten außerdem den Entitätsnamen, den Status (gelöscht/nicht gelöscht), die GUID (wie der Dateiname), die Datenbankrevision (um Datenmigrationen zu erkennen oder eine Synchronisierung mit nie verwendeten App-Versionen zu vermeiden) und natürlich die Daten (wenn die Zeile gelöscht ist) nicht gelöscht).

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 keine Zeit dafür habe :) Im Moment 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 erste Laden in der richtigen Reihenfolge in leere Kerndaten durchzuführen. iCloud funktioniert auf diese Weise, und auch andere Synchronisierungslösungen haben einen ähnlichen Ansatz, z. B. TICoreDataSync .

- AKTUALISIERUNG

Nach einer Weile bin ich zu Ensembles gewechselt - ich empfehle diese Lösung, weil ich das Rad neu erfunden habe.

2
thom_ek