web-dev-qa-db-ja.com

iPhone Core DataをWebサーバーと同期してから、他のデバイスにプッシュする方法は?

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

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

私が考えなければならない空想はありますか? ObjectiveResourceCore Resource 、および RestfulCoreData などの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. また、ローカルデバイスは、サーバーから変更を受信した場合に、メモリに保持されているものを更新する必要があります。

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

290
Jason

IPhone 2009カンファレンスでDan Groverが議論した同期戦略を注意深く読んで実装することをお勧めします。PDFドキュメントとして here を入手できます。

これは実行可能なソリューションであり、実装するのはそれほど難しくありません(Danはこれをアプリケーションのいくつかに実装しました)。Chrisが説明したソリューションと重複しています。同期の詳細な理論的議論については、ラスコックス(MIT)およびウィリアムジョセフソン(プリンストン)の論文を参照してください。

ベクタータイムペアによるファイル同期

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

編集:

Groverのpdfファイルはもう利用できないようです(リンク切れ、2015年3月)。更新:リンクはWay Back Machineから利用できます here

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

141
Massimo Cafaro

あなたがやろうとしていることと似たようなことをしました。私が学んだことと、それをどうやってやったか教えてください。

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

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

  1. sync_status-コアデータモデルのみにこのフィールドを追加します。アイテムに保留中の変更があるかどうかを判断するために、アプリによって使用されます。次のコードを使用します。0は変更なし、1はサーバーと同期するためにキューに入れられ、2は一時オブジェクトであり、パージできることを意味します。
  2. is_deleted-これをサーバーおよびコアデータモデルに追加します。削除イベントは、データベースまたはクライアントモデルから実際に行を削除するべきではありません。これは、同期して戻すものが何もないためです。この単純なブールフラグを使用することで、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を生成する必要があります。

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

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

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

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

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

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

それがお役に立てば幸いです。 Wordのレコードとモデルは同じ意味で使用しましたが、アイデアは得られたと思います。がんばろう。

269
chris

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

11
radiospiel

@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 を参照してください。

7
knagode

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

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

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

回答として投稿したくありませんでしたが、コメントとして表示される人がいることは知りません。このトピックにとって重要であり、他の回答には含まれないと思います。

5
eselk

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

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

IOS 6 SDKでビルドします。2012年9月27日の時点で最新です。

5
logan

プッシュ通知を介してデータを更新するようユーザーに通知します。アプリでバックグラウンドスレッドを使用して、ローカルデータとクラウドサーバー上のデータを確認します。一方、サーバーで変更が発生した場合、ローカルデータを変更します。

したがって、最も難しい部分は、どちらの側が無効化されているかを推定することです。

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

5
Stan

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

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

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

-更新

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

2
thom_ek