iPhoneコアデータをWebサーバーと同期し、他のデバイスにプッシュする方法 [閉まっている]


293

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

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

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

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

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

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

他にここで見逃しているものはありますか?これを可能にするには、どのようなフレームワークに注目すべきですか?


5
プッシュ通知の受信に依存することはできません。ユーザーはそれらをタップするだけで、2番目の通知が到着すると、OSは最初の通知を破棄します。いずれにしても、IMOプッシュ通知は同期更新を受信するための悪い方法です。これは、ユーザーに割り込みをかけるためです。アプリは、起動されるたびに同期を開始する必要があります。
Ole Begemann、2011

OK。情報をありがとう-常にサーバーをポーリングし、起動時に更新を確認する以外に、デバイスが更新を取得する方法はありますか?アプリが複数のデバイスで同時に開いている場合に機能させることに興味があります。
ジェイソン

1
(私は少し遅れて知っていますが、誰かがこれに遭遇した場合や不思議にも思います)複数のデバイスを同時に同期させるには、他のデバイスまたはサーバーとのオープン接続を維持し、メッセージを送信して他のデバイスに通知することができます)更新が発生したとき。(例:IRC /インスタントメッセージングのしくみ)
Dan2552 2012年

1
@ Dan2552:あなたが説明するものは[ロングポーリング] [ en.wikipedia.org/wiki/ ]として知られ、素晴らしいアイデアですが、オープン接続はモバイルデバイスでかなりのバッテリーと帯域幅を消費します。
johndodo 2013年

1
ここではどのようにあなたのアプリとウェブサービスとの間の同期データへのレイWenderlichから良いチュートリアルです:raywenderlich.com/15916/...
JRG-開発

回答:


144

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

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

ベクトル時間ペアとのファイル同期

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

編集:

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

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


ZSyncビデオの更新されたリンクを持っている人はいますか?また、ZSyncはまだ維持されていますか?私は、それが2010年に最後に更新された参照
ジェレミーWeldin

ZSyncのgithubでの最後のコミットは2010年9月でした。そのため、マーカスはそれをサポートしなくなったと思います。
生鮮デーブ

1
Dan Groverによって記述されたアルゴリズムは非常に優れています。ただし、新しい更新のチェックに時間が使用されているときにクライアントが更新を見逃さないようにする方法がないため、マルチスレッドサーバーコードでは機能しません(したがって、これはまったくスケーリングされません)。 。私が間違っている場合は私を修正してください-これが機能する実装を見ると殺されます。
masi

1
@Patt、私はちょうどあなたに要求されたように、あなたにpdfファイルを送りました。乾杯、マッシモカファロ。
Massimo Cafaro 2015年

3
不足しているクロスプラットフォームデータ同期 PDFスライドは、Dan GroverによってWayback Machineからアクセスできます。
Matthew Kairys、2015

272

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

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を生成する必要があります。

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

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

サーバーはリクエストを受け取り、これを行います:

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

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

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

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


2
last_modifiedフィールドはローカルデータベースにも存在しますが、iPhoneの時計では更新されません。これはサーバーによって設定され、同期されます。MAX(last_modified)日付は、アプリがサーバーに送信する日付で、その日付以降に変更されたすべてのものを返送するように指示します。
クリス

3
クライアントのグローバル値はを置き換えるMAX(last_modified)ことができますが、MAX(last_modified)それで十分なので、それは冗長になります。にsync_statusは別の役割があります。先に書いたように、MAX(last_modified)サーバーからsync_status何を同期する必要があるかを決定し、サーバーに何を同期する必要があるかを決定します。
クリス、2013

2
@Flex_Addictedありがとう。はい、同期するエンティティごとにフィールドを複製する必要があります。ただし、モデルを関係(1対多など)と同期する場合は、さらに注意する必要があります。
chris

2
@BenPackard-あなたは正しいです。このアプローチは競合の解決を行わないため、最後のクライアントが勝ちます。レコードは1人のユーザーによって編集されるため、アプリでこれに対処する必要はありませんでした。あなたがこれをどのように解決するか知りたいです。
クリス2013年

2
こんにちは@noilly、次のケースを検討してください:ローカルオブジェクトに変更を加え、それをサーバーに同期する必要があります。同期は数時間後または数日後にのみ発生し(しばらくオフラインであった場合など)、その間にアプリがシャットダウンされ、数回再起動された可能性があります。この場合、NSManagedObjectContextのメソッドはあまり役に立ちません。
クリス2014

11

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


3
これは、データをリレーショナルデータではなくドキュメントとして表現できる場合にのみ必要なことを行います。回避策はありますが、それらが常にきれいであるとは限りません。
Jeremie Weldin、2011年

小さなアプリケーションにはドキュメントで十分です
Hai Feng Kao

@radiospielリンクが壊れています
Mick

これにより、バックエンドをCouchbase DBに書き込む必要がある依存関係も追加されます。同期のためのNOSQLのアイデアから始めましたが、バックエンドでMS SQLを実行しているため、バックエンドをNOSQLに制限できません。
thesummersign 2015

@ミック:再び動作するようです(または誰かがリンクを修正しましたか?ありがとう)
radiospiel

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でソース、実際の例、および詳細な手順を見つけることができます。


デバイスの時間を異常な値に変更しても大丈夫ですか?
Golden

5

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

だから一番難しいのは、どちら側が無効なのかを見積もることだと思います。

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


5

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

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

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


5
Stack Overflowへようこそ!回答を投稿していただきありがとうございます。セルフプロモーションに関するFAQをよくお読みください。
アンドリューバーバー

5

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

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

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

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


2

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

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

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

-更新

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.