299

私は、iPhoneアプリケーションに保存されているコアデータをiPadやMacなどの複数のデバイス間で同期する方法に取り組んでいます。iOSのCoreDataで使用する同期フレームワークは(あるとしても)多くありません。しかし、私は次の概念について考えてきました。

  1. ローカルコアデータストアに変更が加えられ、変更が保存されます。(a)デバイスがオンラインの場合、変更セットを送信したデバイスのデバイスIDを含め、変更セットをサーバーに送信しようとします。(b)チェンジセットがサーバーに到達しない場合、またはデバイスがオンラインでない場合、アプリは変更セットをキューに追加して、オンラインになったときに送信します。
  2. クラウドにあるサーバーは、受け取った特定の変更セットをマスターデータベースとマージします。
  3. 変更セット(または変更セットのキュー)がクラウドサーバーにマージされた後、サーバーは、ある種のポーリングシステムを使用して、サーバーに登録されている他のデバイスにそれらの変更セットをすべてプッシュします。(私はAppleのプッシュサービスを使用することを考えましたが、コメントによると、これは実行可能なシステムではないようです。)

私が考える必要がある何か空想はありますか?ObjectiveResourceCore ResourceRestfulCoreDataなどのRESTフレームワークを見てきました。もちろん、これらはすべてRuby on Railsで動作しますが、私はこれに縛られていませんが、ここから始めることができます。私のソリューションに対する主な要件は次のとおりです。

  1. 変更は、メインスレッドを一時停止せずにバックグラウンドで送信する必要があります。
  2. 使用する帯域幅はできるだけ少なくする必要があります。

私はいくつかの課題について考えてきました。

  1. さまざまなデバイス上のさまざまなデータストアのオブジェクトIDがサーバーに接続されていることを確認してください。つまり、データベースに格納されているオブジェクトへの参照を介して関連付けられたオブジェクトIDとデバイスIDのテーブルがあります。レコード(DatabaseId [このテーブルに固有]、ObjectId [データベース全体のアイテムに固有]、Datafield1、Datafield2)があり、ObjectIdフィールドは別のテーブルAllObjects:(ObjectId、DeviceId、DeviceObjectId)を参照します。次に、デバイスが変更セットをプッシュすると、ローカルデータストアのコアデータオブジェクトからデバイスIDとobjectIdが渡されます。次に、クラウドサーバーはAllObjectsテーブルのobjectIdとデバイスIDをチェックし、初期テーブルで変更するレコードを見つけます。
  2. マージできるように、すべての変更にタイムスタンプを付ける必要があります。
  3. デバイスは、バッテリーを使いすぎずにサーバーをポーリングする必要があります。
  4. サーバーから変更を受信した場合、ローカルデバイスは、メモリに保持されているものをすべて更新する必要があります。

私がここで見逃しているものは他にありますか?これを可能にするには、どのようなフレームワークを検討する必要がありますか?

4

8 に答える 8

277

私はあなたがやろうとしていることに似た何かをしました。私が学んだこととそれをどのようにしたかをお話ししましょう。

Core Dataオブジェクトとサーバー上のモデル(またはdbスキーマ)の間に1対1の関係があると仮定します。サーバーの内容をクライアントと同期させたいだけですが、クライアントはデータを変更および追加することもできます。私がそれを正しく理解したなら、それから読み続けてください。

同期を支援するために4つのフィールドを追加しました。

  1. sync_status-このフィールドをコアデータモデルにのみ追加します。これは、アイテムに保留中の変更があるかどうかを判断するためにアプリによって使用されます。次のコードを使用します。0は変更がないことを意味し、1はサーバーと同期するためにキューに入れられていることを意味し、2は一時オブジェクトでありパージできることを意味します。
  2. is_deleted-これをサーバーとコアデータモデルに追加します。Deleteイベントは、データベースまたはクライアントモデルから行を実際に削除するべきではありません。これは、同期を戻すものが何もないためです。この単純なブールフラグを使用することで、is_deletedを1に設定して同期することができ、誰もが満足するでしょう。また、サーバーとクライアントのコードを変更して、「is_deleted=0」で削除されていないアイテムをクエリする必要があります。
  3. last_modified-これをサーバーとコアデータモデルに追加します。このフィールドは、そのレコードで何かが変更されるたびに、サーバーによって現在の日付と時刻で自動的に更新される必要があります。クライアントが変更しないでください。
  4. guid-サーバーとコアデータモデルにグローバル一意ID(http://en.wikipedia.org/wiki/Globally_unique_identifierを参照)フィールドを追加します。このフィールドは主キーになり、クライアントで新しいレコードを作成するときに重要になります。通常、主キーはサーバー上の増分整数ですが、コンテンツはオフラインで作成され、後で同期される可能性があることに注意する必要があります。GUIDを使用すると、オフライン中にキーを作成できます。

クライアントで、何かが変更されてサーバーに同期する必要がある場合は常に、モデルオブジェクトでsync_statusを1に設定するコードを追加します。新しいモデルオブジェクトはGUIDを生成する必要があります。

同期は単一のリクエストです。リクエストには次のものが含まれます。

  • モデルオブジェクトのMAXlast_modifiedタイムスタンプ。これは、このタイムスタンプの後でのみ変更が必要であることをサーバーに通知します。
  • sync_status=1のすべてのアイテムを含むJSON配列。

サーバーはリクエストを取得し、これを実行します。

  • JSON配列からコンテンツを取得し、そこに含まれるレコードを変更または追加します。last_modifiedフィールドは自動的に更新されます。
  • サーバーは、リクエストで送信されたタイムスタンプよりも大きいlast_modifiedタイムスタンプを持つすべてのオブジェクトを含むJSON配列を返します。これには、受信したばかりのオブジェクトが含まれます。これは、レコードがサーバーに正常に同期されたことの確認応答として機能します。

アプリは応答を受け取り、これを行います。

  • JSON配列からコンテンツを取得し、そこに含まれるレコードを変更または追加します。各レコードは、sync_status0に設定されます。

それがお役に立てば幸いです。レコードとモデルという言葉を同じ意味で使用しましたが、あなたはその考えを理解していると思います。幸運を。

于 2011-02-19T17:07:28.710 に答える
146

私は、iPhone2009カンファレンスでDanGroverが議論した同期戦略を注意深く読み、実装することをお勧めします。これは、PDFドキュメントとしてここで入手できます。

これは実行可能なソリューションであり、実装するのはそれほど難しくありません(Danはこれをいくつかのアプリケーションに実装しました)。Chrisによって説明されたソリューションと重複しています。同期に関する詳細で理論的な議論については、Russ Cox(MIT)とWilliam Josephson(Princeton)の論文を参照してください。

ベクトル時間ペアを使用したファイル同期

これは、いくつかの明らかな変更を加えて、コアデータにも同様に適用されます。これにより、全体的にはるかに堅牢で信頼性の高い同期戦略が提供されますが、正しく実装するにはより多くの労力が必要になります。

編集:

Groverのpdfファイルは利用できなくなったようです(リンク切れ、2015年3月)。更新:リンクは、こちらのWayBackMachineから入手できます。

ZSyncと呼ばれMarcusZarraによって開発されたObjective-Cフレームワークは、iCloudが最終的に正しいコアデータ同期をサポートしているように見えるため、非推奨になりました。

于 2011-03-02T08:04:53.460 に答える
11

それでも移動方法を探している場合は、Couchbasemobileを調べてください。これは基本的にあなたが望むすべてを行います。(http://www.couchbase.com/nosql-databases/couchbase-mobile

于 2011-11-02T12:02:35.257 に答える
7

@Crisと同様に、クライアントとサーバー間の同期用のクラスを実装し、これまでのすべての既知の問題を解決しました(サーバーとの間でデータを送受信する、タイムスタンプに基づいて競合をマージする、信頼性の低いネットワーク条件で重複するエントリを削除する、ネストされたデータを同期する、ファイルなど..)

どのエンティティとどの列を同期する必要があるか、サーバーはどこにあるかをクラスに指示するだけです。

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];

ソース、作業例、その他の手順については、github.com / knagode/ M3Synchronizationをご覧ください。

于 2013-08-21T16:57:43.220 に答える
5

プッシュ通知を介してデータを更新するようにユーザーに通知します。アプリのバックグラウンドスレッドを使用して、ローカルデータとクラウドサーバー上のデータを確認します。サーバーで変更が行われている間、ローカルデータを変更します。その逆も同様です。

ですから、最も難しいのは、どちら側が無効になっているのかを推定することだと思います。

これがあなたを助けることができることを願っています

于 2011-02-18T09:46:35.460 に答える
5

SynCloudと呼ばれる新しいCoreDataCloudSyncingAPIの最初のバージョンを投稿しました。SynCloudはマルチユーザー同期インターフェースを可能にするため、iCloudとは多くの違いがあります。また、マルチテーブルのリレーショナルデータを使用できるため、他の同期APIとは異なります。

詳細については、http://www.syncloudapi.comをご覧ください。

iOS 6 SDKを使用してビルドします。これは、2012年9月27日時点で非常に最新です。

于 2012-09-27T14:38:17.793 に答える
5

GUIDの問題に対する良い解決策は「分散IDシステム」だと思います。正しい用語が何であるかはわかりませんが、MS SQLサーバーのドキュメントでそれを呼んでいたと思います(SQLは分散/同期データベースにこのメソッドを使用/使用しました)。とても簡単です。

サーバーはすべてのIDを割り当てます。同期が行われるたびに、最初にチェックされるのは「このクライアントに残っているIDの数」です。クライアントが不足している場合は、サーバーにIDの新しいブロックを要求します。次に、クライアントはその範囲のIDを新しいレコードに使用します。これは、次の同期の前に「決して」不足しないように十分な大きさのブロックを割り当てることができ、サーバーが時間の経過とともに使い果たされるほど大きくない場合、ほとんどのニーズに最適です。クライアントが不足した場合、処理は非常に簡単です。「同期するまでアイテムを追加できません」とユーザーに伝えてください...多くのアイテムを追加する場合は、古いデータを避けるために同期しないでください。とにかく問題?

ランダムGUIDは100%安全ではなく、通常は標準IDよりもはるかに長くする必要があるため(128ビットと32ビット)、これはランダムGUIDを使用するよりも優れていると思います。通常、IDによるインデックスがあり、多くの場合ID番号をメモリに保持するため、それらを小さく保つことが重要です。

本当に答えとして投稿したくなかったのですが、誰かがコメントとして見るかどうかはわかりません。このトピックにとって重要であり、他の回答には含まれていないと思います。

于 2014-09-10T18:59:24.520 に答える
2

まず、データ、テーブル、リレーションの数を再考する必要があります。私のソリューションでは、Dropboxファイルを介した同期を実装しました。メインMOCの変更を観察し、これらのデータをファイルに保存します(各行はgzip圧縮されたjsonとして保存されます)。インターネット接続が機能している場合は、Dropboxに変更があるかどうかを確認し(Dropboxはデルタ変更を提供します)、それらをダウンロードしてマージし(最新の勝利)、最後に変更されたファイルを配置します。同期する前に、他のクライアントが不完全なデータを同期するのを防ぐために、Dropboxにロックファイルを置きました。変更をダウンロードするときは、部分的なデータのみをダウンロードしても安全です(インターネット接続の喪失など)。ダウンロードが(完全にまたは部分的に)完了すると、CoreDataへのファイルのロードが開始されます。未解決の関係がある場合(すべてのファイルがダウンロードされるわけではありません)、ファイルのロードを停止し、後でダウンロードを終了しようとします。リレーションはGUIDとしてのみ保存されるため、完全なデータ整合性を確保するためにロードするファイルを簡単に確認できます。コアデータに変更が加えられた後、同期が開始されます。変更がない場合は、数分ごとにDropboxとアプリの起動時に変更をチェックします。さらに、変更がサーバーに送信されるときに、変更について通知するために他のデバイスにブロードキャストを送信して、それらがより高速に同期できるようにします。同期された各エンティティにはGUIDプロパティがあります(GUIDは交換ファイルのファイル名としても使用されます)。各ファイルのDropboxリビジョンを保存する同期データベースもあります(Dropboxデルタが状態をリセットするときに比較できます)。ファイルには、エンティティ名、状態(削除済み/削除なし)、GUID(ファイル名と同じ)、データベースリビジョン(データ移行を検出するため、またはアプリバージョンとの同期を回避するため)、そしてもちろんデータ(行が削除されていない場合)も含まれます。そのため、完全なデータ整合性を実現するためにロードするファイルを簡単に確認できます。コアデータに変更が加えられた後、同期が開始されます。変更がない場合は、数分ごとにDropboxとアプリの起動時に変更をチェックします。さらに、変更がサーバーに送信されるときに、変更について通知するために他のデバイスにブロードキャストを送信して、それらがより高速に同期できるようにします。同期された各エンティティにはGUIDプロパティがあります(GUIDは交換ファイルのファイル名としても使用されます)。各ファイルのDropboxリビジョンを保存する同期データベースもあります(Dropboxデルタが状態をリセットするときに比較できます)。ファイルには、エンティティ名、状態(削除済み/削除なし)、GUID(ファイル名と同じ)、データベースリビジョン(データ移行を検出するため、またはアプリバージョンとの同期を回避するため)、そしてもちろんデータ(行が削除されていない場合)も含まれます。そのため、完全なデータ整合性を実現するためにロードするファイルを簡単に確認できます。コアデータに変更が加えられた後、同期が開始されます。変更がない場合は、数分ごとにDropboxとアプリの起動時に変更をチェックします。さらに、変更がサーバーに送信されるときに、変更について通知するために他のデバイスにブロードキャストを送信して、それらがより高速に同期できるようにします。同期された各エンティティにはGUIDプロパティがあります(GUIDは交換ファイルのファイル名としても使用されます)。各ファイルのDropboxリビジョンを保存する同期データベースもあります(Dropboxデルタが状態をリセットするときに比較できます)。ファイルには、エンティティ名、状態(削除済み/削除なし)、GUID(ファイル名と同じ)、データベースリビジョン(データ移行を検出するため、またはアプリバージョンとの同期を回避するため)、そしてもちろんデータ(行が削除されていない場合)も含まれます。

このソリューションは、数千のファイルと約30のエンティティで機能します。Dropboxの代わりに、後でやりたいREST Webサービスとしてキー/値ストアを使用できますが、これには時間がありません:)今のところ、私の意見では、私のソリューションはiCloudよりも信頼性が高く、これは非常に重要です。私はそれがどのように機能するかを完全に制御できます(主にそれが私自身のコードであるため)。

もう1つの解決策は、MOCの変更をトランザクションとして保存することです。サーバーと交換されるファイルははるかに少なくなりますが、空のコアデータに適切な順序で初期ロードを行うのは困難です。iCloudはこのように機能しており、 TICoreDataSyncなどの他の同期ソリューションにも同様のアプローチがあります。

- アップデート

しばらくして、私はEnsemblesに移行しました-車輪の再発明よりもこのソリューションをお勧めします。

于 2014-03-06T20:59:18.563 に答える