web-dev-qa-db-ja.com

コアデータとa REST= Webサービスを非同期で同期すると同時に、すべてのREST=エラーをUIに適切に伝播する方法

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

要件の一部は次のとおりです。

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

サーバー駆動型のユーザーエクスペリエンスの構築に関するWWDC10セッション117をチェックアウトし、 Objective Resource =、 コアリソース 、および RestfulCoreData フレームワーク。

Objective Resourceフレームワークはそれ自体ではCore Dataと通信せず、単にREST=クライアント実装です。CoreResourceとRestfulCoreDataはすべて、コード内でCore Dataと通信することを前提としており、すべて解決しますモデルレイヤーの背景にあるナットとボルト。

これまでのところすべてが大丈夫に見え、最初はCore ResourceまたはRestfulCoreDataのいずれかが上記の要件のすべてをカバーしますが、...それらがどれも正しく解決されないように思われるいくつかのことがあります:

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

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

RestfulCoreDataは-save:呼び出しをそのまま保持し、クライアントスレッドに追加の待機時間を導入しません。単にNSManagedObjectContextDidSaveNotificationを監視し、対応するリクエストを通知ハンドラーでサーバーに発行します。しかし、このように-save:呼び出しは常に正常に完了します(保存された変更でCore Dataに問題がない場合)。いくつかの404または421またはその他のサーバー側エラーが発生しました。さらに、ローカルストレージではデータが更新されますが、サーバーは変更を認識しません。不合格。

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

  1. ネットワーク要求が発生している間、呼び出しスレッドが-save:呼び出しごとにブロックしないようにします。
  2. 何らかの同期操作が失敗したという通知をUIで何らかの形で受け取りたいと思います。
  3. サーバー要求が失敗した場合、実際のコアデータの保存も失敗するようにします。

何か案は?

84
eploko

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

26
Blake Watters

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に通知することもできます。

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

18

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

コアデータ:データの効率的なインポート

コアデータ:変更管理

コアデータ:コアデータを使用したマルチスレッド

2
David Weiss

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

クライアント側の相関保存については、そのデータを保持するだけで、サーバーからOKになるまで保存せずに、ロールバックオプションの種類を確認する必要があります-削除方法はサーバー障害です。

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

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

0
ZXX