2010-06-19 85 views
82

嘿,我正在爲我們的應用程序的模型層工作。如何異步同步CoreData和REST Web服務,並同時將任何REST錯誤正確傳播到UI中

一些的要求是這樣的:

  1. 應該在iPhone OS 3.0+工作。
  2. 我們的數據來源是一個RESTful Rails應用程序。
  3. 我們應該使用核心數據在本地緩存數據。
  4. 客戶端代碼(我們的UI控制器)應該儘可能少地瞭解任何網絡內容,並且應該使用Core Data API查詢/更新模型。

我已經簽出了WWDC10會話117上建立一個服務器,驅動用戶體驗,花了一些時間檢查出Objective ResourceCore ResourceRestfulCoreData框架。

Objective Resource框架本身不與Core Data交談,僅僅是一個REST客戶端實現。 Core Resource和RestfulCoreData都假設你在代碼中與Core Data交談,他們解決了模型層背景中的所有問題。

所有看起來不錯,到目前爲止,最初我雖然無論是核心資源或RestfulCoreData將覆蓋所有的上述要求,但......有一對夫婦沒有他們的事情看似偶然正確地解決:

  1. 在保存本地更新到服務器時,主線程不應被阻塞。
  2. 如果保存操作失敗,應將錯誤傳播到UI,並且不應將更改保存到本地Core Data存儲。

核心資源發生在你對你的管理對象情境呼叫- (BOOL)save:(NSError **)error發出所有的請求到服務器,因此能夠提供底層請求到服務器的正確NSError實例失敗不知何故。但是它會阻止調用線程,直到保存操作結束。失敗。

RestfulCoreData保持您的-save:調用不變,並且不會爲客戶端線程帶來任何額外的等待時間。它僅僅注意到NSManagedObjectContextDidSaveNotification,然後在通知處理程序中向服務器發出相應的請求。但這樣-save:調用總是成功完成(當然,如果Core Data與已保存的更改一致,並且實際調用它的客戶端代碼無法知道保存可能無法傳播到服務器,因爲某些404421或發生任何服務器端錯誤。而且,本地存儲變得更新了數據,但服務器從來不知道這些變化。失敗。

所以,我在處理這些問題尋找一個可能的解決方案/常見的做法:

  1. 我不想調用線程上的每個-save:呼叫阻塞而網絡請求發生。
  2. 我想以某種方式在UI中獲取某些同步操作出錯的通知。
  3. 我想實際的核心數據保存失敗,以及如果服務器請求失敗。

任何想法?

+1

哇,你不知道你通過問這個問題救了我多少麻煩。我目前實現了我的應用程序,讓用戶在每次撥打電話時都等待數據(儘管是.NET Web服務)。我一直在想辦法讓它異步,但不知道如何去做。感謝您提供的所有資源! – 2010-07-27 14:25:00

+0

優秀的問題,謝謝。 – Justin 2010-07-28 12:25:03

+0

核心資源的鏈接已損壞,有誰知道它現在託管在哪裏? – 2012-05-08 09:00:20

回答

26

對於這個用例,你應該看看RestKit(http://restkit.org)。它旨在解決將遠程JSON資源建模和同步到本地核心數據支持緩存的問題。當沒有網絡可用時,它支持完全從緩存工作的離線模式。所有同步都發生在後臺線程上(網絡訪問,有效負載解析和託管對象上下文合併),並且有一組豐富的委託方法,因此您可以知道發生了什麼。

0

你需要那將會給其他線程上運行一個回調函數(就是實際的服務器交互發生後),然後把結果代碼/錯誤信息將由UI線程定期檢查一個半全局數據。請確保作爲標誌是原子,或者你將有一個競爭條件數的wirting - 說,如果你的錯誤響應是你需要一個int(whihc應該有原子ACCES)32個字節,然後你把這個int在off/false/not-ready狀態下,直到你的大數據塊被寫入,然後寫「true」來翻轉開關,這樣說話。

對於客戶端的相關保存,你必須保存這些數據並保存它,直到你從服務器上得到OK,確保你有一個kinnf的回滾選項 - 比如說一個刪除的方法是server失敗。

請注意,除非您執行完整的兩階段提交程序(客戶端保存或刪除可能會在來自服務器服務器的信號後失敗),否則將永遠不會100%安全,但這會花費您2次前往服務器的時間至少(如果你的唯一回滾選項是刪除,可能會花費你4美元)。

理想情況下,你會做的運行的全阻斷版本在一個單獨的線程,但你需要4.0爲。

2

這就實現了同步問題,而不是一個容易解決的問題。下面是我要做的:在iPhone UI中使用一個上下文,然後使用另一個上下文(和另一個線程)從Web服務下載數據。一旦完成所有的工作,就會經歷下面推薦的同步/導入過程,然後在所有內容正確導入後刷新UI。如果訪問網絡時出現問題,只需回滾非UI上下文中的更改即可。這是一大堆工作,但我認爲這是最好的方法。

Core Data: Efficiently Importing Data

Core Data: Change Management

Core Data: Multi-Threading with Core Data

18

有三個基本組成部分:

  1. 的UI行動和堅持,最多改到服務器的變化CoreData
  2. 持久保留
  3. 刷新與服務器

一個的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 

協議格式當然取決於你的具體使用情況,但基本上就是你要創建的是一個機制哪些更改可以「推送」到您的服務器。

接下來還有用戶界面環來考慮,保持代碼乾淨就好調用保存;又有自動推到服務器上的變化。你可以使用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,你可以簡單地搶實體編號從保存的通知,並在後臺線程創建的操作明顯滯後。

如果您的REST API支持批處理,您甚至可以一次發送整個陣列,然後通知您UI已同步多個實體。

我預見到的唯一問題是,沒有「真正的」解決方案是用戶不希望等待他們的更改被推送到服務器以允許進行更多的更改。我遇到的唯一好範例是,您允許用戶不斷編輯對象,並在適當的時候將其編輯合併在一起,即不會推送每個保存通知。