CoreDataとRESTWebサービスを非同期で同期すると同時に、RESTエラーをUIに適切に伝播する方法


85

ねえ、私はここで私たちのアプリのモデルレイヤーに取り組んでいます。

要件のいくつかは次のようなものです。

  1. iPhone OS3.0以降で動作するはずです。
  2. データのソースはRESTfulRailsアプリケーションです。
  3. CoreDataを使用してデータをローカルにキャッシュする必要があります。
  4. クライアントコード(UIコントローラー)は、ネットワークに関する知識をできるだけ少なくし、Core DataAPIを使用してモデルをクエリ/更新する必要があります。

私がチェックアウトしましたWWDC10セッション117をチェックアウトするいくつかの時間を費やしサーバー主導のユーザーエクスペリエンス、建物の上に目的のリソースコアリソース、およびRestfulCoreDataの枠組みを。

Objective Resourceフレームワークは、それ自体ではCore Dataと通信せず、RESTクライアントの実装にすぎません。Core ResourceとRestfulCoreDataはすべて、コード内のCore Dataと通信することを前提としており、モデルレイヤーのバックグラウンドですべての要点を解決します。

これまでのところすべて問題ないように見えますが、最初はCore ResourceまたはRestfulCoreDataのいずれかで上記の要件がすべてカバーされますが、...正しく解決されていないように見えることがいくつかあります。

  1. ローカル更新をサーバーに保存している間は、メインスレッドをブロックしないでください。
  2. 保存操作が失敗した場合、エラーはUIに伝播され、変更はローカルCoreDataストレージに保存されません。

コアリソースは- (BOOL)save:(NSError **)error、マネージドオブジェクトコンテキストを呼び出すと、サーバーにすべてのリクエストを発行するため、サーバーへの基になるリクエストの正しいNSErrorインスタンスを提供できます。ただし、保存操作が終了するまで、呼び出し元のスレッドをブロックします。不合格。

RestfulCoreDataは-save:呼び出しをそのまま維持し、クライアントスレッドに追加の待機時間を導入しません。を監視しNSManagedObjectContextDidSaveNotification、通知ハンドラーでサーバーに対応する要求を発行するだけです。しかし、この方法-save:呼び出しは常に(コアデータが保存された変更で大丈夫です与え、ウェル)正常に完了し、実際にそれがあるため、いくつかのサーバーに伝播しなかったかもしれない保存知る方法がありませんと呼ばれるクライアントコード404またはを421または何をサーバー側のエラーが発生しました。さらに、ローカルストレージはデータを更新するようになりますが、サーバーは変更を認識しません。不合格。

だから、私はこれらすべての問題に対処するための可能な解決策/一般的な慣行を探しています:

  1. 呼び出し元のスレッドがそれぞれをブロックしたくない -save:ネットワーク要求が発生している間、呼び出しでない。
  2. 同期操作が失敗したという通知をUIで取得したいのですが。
  3. サーバー要求が失敗した場合、実際のCoreDataの保存も失敗するようにします。

何か案は?


1
うわー、あなたはこの質問をすることによってあなたがどれほどのトラブルを救ったのか分かりません。私は現在、電話をかけるたびにユーザーがデータを待機するようにアプリを実装しています(.NET Webサービスではありますが)。私はそれを非同期にする方法を考えていましたが、その方法を理解できませんでした。あなたが提供したすべてのリソースに感謝します!
Tejaswi Yerukalapudi 2010

すばらしい質問です、ありがとうございます。
ジャスティン

コアリソースへのリンクが壊れています、誰かがそれが今どこでホストされているか知っていますか?

コアリソースは引き続きGitHubでホストされています:github.com/mikelaurence/CoreResource
eploko

また、元のサイトはgitHubにもあります:github.com/mikelaurence/coreresource.org
eploko

回答:


26

このユースケースについては、RestKit(http://restkit.org)を実際に確認する必要があります。これは、リモートJSONリソースのモデリングとローカルCoreDataでバックアップされたキャッシュへの同期の問題を解決するように設計されています。利用可能なネットワークがない場合に、キャッシュから完全に作業するためのオフラインモードをサポートします。すべての同期はバックグラウンドスレッド(ネットワークアクセス、ペイロード解析、および管理対象オブジェクトコンテキストのマージ)で行われ、何が起こっているかを知ることができるように、デリゲートメソッドの豊富なセットがあります。


18

3つの基本的なコンポーネントがあります。

  1. UIアクションとCoreDataへの変更の永続化
  2. その変更をサーバーまで永続化する
  3. サーバーの応答でUIを更新する

NSOperation + NSOperationQueueは、ネットワーク要求を整然と保つのに役立ちます。デリゲートプロトコルは、UIクラスがネットワークリクエストの状態を理解するのに役立ちます。

@protocol NetworkOperationDelegate
  - (void)operation:(NSOperation *)op willSendRequest:(NSURLRequest *)request forChangedEntityWithId:(NSManagedObjectID *)entity;
  - (void)operation:(NSOperation *)op didSuccessfullySendRequest:(NSURLRequest *)request forChangedEntityWithId:(NSManagedObjectID *)entity;
  - (void)operation:(NSOperation *)op encounteredAnError:(NSError *)error afterSendingRequest:(NSURLRequest *)request forChangedEntityWithId:(NSManagedObjectID *)entity;
@end

プロトコル形式はもちろん特定のユースケースによって異なりますが、基本的に作成しているのは、変更をサーバーに「プッシュ」できるメカニズムです。

次に、考慮すべきUIループがあります。コードをクリーンに保つには、save:を呼び出して、変更をサーバーに自動的にプッシュすることをお勧めします。これにはNSManagedObjectContextDidSave通知を使用できます。

- (void)managedObjectContextDidSave:(NSNotification *)saveNotification {
  NSArray *inserted = [[saveNotification userInfo] valueForKey:NSInsertedObjects];
  for (NSManagedObject *obj in inserted) {
    //create a new NSOperation for this entity which will invoke the appropraite rest api
    //add to operation queue
  }

  //do the same thing for deleted and updated objects
}

ネットワーク操作を挿入するための計算オーバーヘッドはかなり低いはずですが、UIに顕著な遅延が生じる場合は、保存通知からエンティティIDを取得して、バックグラウンドスレッドで操作を作成するだけで済みます。

REST APIがバッチ処理をサポートしている場合は、配列全体を一度に送信してから、複数のエンティティが同期されたことをUIに通知することもできます。

私が予測し、「実際の」解決策がない唯一の問題は、ユーザーが変更をサーバーにプッシュしてさらに変更できるようになるのを待ちたくないということです。私が遭遇した唯一の良いパラダイムは、ユーザーがオブジェクトを編集し続け、必要に応じて編集をまとめてバッチ処理できるようにすることです。つまり、すべての保存通知をプッシュするわけではありません。


2

これは同期の問題になり、簡単に解決することはできません。これが私がすることです:あなたのiPhone UIで1つのコンテキストを使用し、次に別のコンテキスト(および別のスレッド)を使用してWebサービスからデータをダウンロードします。すべてが完了したら、以下で推奨される同期/インポートプロセスを実行し、すべてが正しくインポートされた後でUIを更新します。ネットワークへのアクセス中に問題が発生した場合は、UI以外のコンテキストで変更をロールバックするだけです。たくさんの作業ですが、アプローチするのに最適な方法だと思います。

コアデータ:データを効率的にインポートする

コアデータ:変更管理

Core Data:CoreDataを使用したマルチスレッド


0

他のスレッド(実際のサーバーの相互作用が発生するスレッド)で実行されるコールバック関数が必要です。次に、結果コード/エラー情報を、UIスレッドによって定期的にチェックされるセミグローバルデータに配置します。フラグとして機能する数値のワーティングがアトミックであることを確認してください。そうしないと、競合状態が発生します。たとえば、エラー応答が32バイトの場合、intが必要です(アトミックアクセスが必要です)。その後、そのintを保持します。より大きなデータブロックが書き込まれるまでoff / false / not-ready状態になり、その後「true」と書き込んで、いわばスイッチを切り替えます。

クライアント側での相関保存の場合は、そのデータを保持し、サーバーからOKになるまで保存しないでください。ロールバックオプションの種類があることを確認してください。たとえば、削除する方法がサーバーに失敗した場合です。

完全な2フェーズコミット手順を実行しない限り、100%安全になることは決してないことに注意してください(サーバーサーバーからのシグナルの後にクライアントの保存または削除が失敗する可能性があります)が、少なくともサーバーへの2回のトリップが必要になります(唯一のロールバックオプションが削除の場合、4の費用がかかる可能性があります)。

理想的には、操作のブロッキングバージョン全体を別のスレッドで実行しますが、そのためには4.0が必要です。

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