ios - 如何將iPhone Core Data與Web服務器同步,然後推送到其他設備?




core-data sync data-synchronization (9)

通知用戶通過推送通知更新數據。 在應用程序中使用後台線程來檢查本地數據和雲服務器上的數據,而在服務器上發生更改時,請更改本地數據,反之亦然。

所以我認為最難的部分是估計哪一方無效的數據。

希望這可以幫助你

我一直在研究一種方法來同步多個設備(如iPad或Mac)之間存儲在iPhone應用程序中的核心數據。 iOS上的Core Data使用的同步框架並不多(如果有的話)。 但是,我一直在思考以下概念:

  1. 對本地核心數據存儲進行更改,並保存更改。 (a)如果設備處於聯機狀態,它會嘗試將更改集發送到服務器,包括發送更改集的設備的設備標識。 (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和objectId。 然後,我的雲服務器將檢查AllObjects表中的objectId和設備Id,並查找要在初始表中更改的記錄。
  2. 所有更改都應該有時間戳,以便它們可以合併。
  3. 該設備將不得不輪詢服務器,而不會消耗太多的電池。
  4. 如果/從服務器接收到更改,本地設備還需要更新內存中的任何內容。

還有什麼我在這裡失踪? 我應該考慮什麼樣的框架來實現這一點?



2017年

關於這個令人難以置信的老問題。

這很像問

“我想購買一部可以隨身攜帶的手機 - 但也可用於許多計算任務,甚至瀏覽WWW!”

顯然,對此的回答是, 如果你已經在火星上,最近在這個星球上實現的主要技術之一是“智能手機”,買一個。

現在,從零開始創建一個OCC系統就像從零開始創建一個SQL數據庫一樣瘋狂。

顯然,對於OCC來說,這是現在所有非平凡應用程序的基本範例

  • 火力地堡
  • PubNub
  • Couchbase

很簡單,是近幾年人類科技的重大進步

今天,你不會再從頭開始創建OCC

  • 編寫你自己的操作系統

  • 從頭編寫自己的SQL數據庫

  • 從頭開始編寫自己的字體渲染

請注意,事實上,從專業意義上講,你不能再成為“ios程序員”或“android程序員”。

誰在乎知道如何佈置表格和按鈕?

您是Firebase /任何專家,並且作為附帶的問題,您知道如何在iOS或Android上佈局按鈕等。

唯一的問題是,哪個BAAS使用 - 例如,如果它是面向遊戲的,可能是PlayFab,如果是真正的消息驅動,可能是PubNub,也許可以.abio.io,如果你是公司 - 也許是kinvey。


我建議您仔細閱讀並實施Dan Grover在iPhone 2009會議上討論的同步策略, here可以通過pdf文檔獲取。

這是一個可行的解決方案,並不難實現(Dan在其幾個應用程序中實現了這一點),重疊了Chris所描述的解決方案。 有關同步的深入理論討論,請參閱Russ Cox(MIT)和William Josephson(Princeton)的論文:

與矢量時間對的文件同步

它同樣適用於核心數據並做了一些明顯的修改。 這提供了一個整體上更加健壯和可靠的同步策略,但需要更多努力才能正確實施。

編輯:

看起來Grover的pdf文件已不再可用(鏈接斷開,2015年3月)。 更新:鏈接可通過Way Back Machine here

由於iCloud最終似乎支持正確的核心數據同步,因此被稱為ZSync並由Marcus Zarra開發的Objective-C框架已被棄用。


首先,您應該重新考慮您將擁有多少數據,表格和關係。 在我的解決方案中,我通過Dropbox文件實現了同步。 我觀察主MOC中的變化並將這些數據保存到文件中(每行保存為gzip json)。 如果有互聯網連接正常工作,我會檢查Dropbox是否有任何更改(Dropbox給我增量更改),下載它們並合併(最新的獲勝),並最終更改已更改的文件。 在同步之前,我把鎖文件放在Dropbox上,以防止其他客戶端同步不完整的數據。 當下載更改時,只安全地下載部分數據是安全的(例如,丟失的互聯網連接)。 當下載完成(完全或部分)時,它開始將文件加載到核心數據。 當存在未解決的關係時(並非所有文件都下載),它將停止加載文件並嘗試稍後完成下載。 關係僅以GUID存儲,因此我可以輕鬆檢查要加載哪些文件以獲得完整的數據完整性。 在對核心數據進行更改之後,正在開始同步。 如果沒有更改,則每隔幾分鐘和應用程序啟動時檢查Dropbox上的更改。 另外,當更改發送到服務器時,我會向其他設備發送廣播以通知其更改,以便它們可以更快地同步。 每個已同步的實體都具有GUID屬性(guid也用作交換文件的文件名)。 我也有同步數據庫,我在那裡存儲每個文件的Dropbox修訂版(我可以在Dropbox delta重置它的狀態時比較它)。 文件還包含實體名稱,狀態(刪除/未刪除),guid(與文件名相同),數據庫修訂(用於檢測數據遷移或避免與從不應用程序版本同步)以及數據(如果行未被刪除)。

該解決方案適用於數千個文件和大約30個實體。 現在,在我看來,我的解決方案比iCloud更加可靠,而且這非常重要,因為我認為我的解決方案比iCloud更可靠,我完全控制它的工作方式(主要是因為它是我自己的代碼)。

另一種解決方案是將MOC更改保存為事務 - 與服務器交換的文件將少得多,但要以空白核心數據的正確順序執行初始加載更為困難。 iCloud正在以這種方式工作,而其他同步解決方案也有類似的方法,例如TICoreDataSync

- 更新

過了一段時間,我遷移到了Ensembles我推薦這個解決方案重新發明輪子。


我做了類似於你想要做的事情。 讓我告訴你我學到了什麼,以及我是如何做到的。

我假設你的核心數據對象和服務器上的模型(或數據庫模式)之間有一對一的關係。 您只需保持服務器內容與客戶端同步,但客戶端也可以修改和添加數據。 如果我說得對,那麼繼續閱讀。

我添加了四個字段來協助同步:

  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字段會自動更新。
  • 服務器返回一個JSON數組,其中包含所有對象的last_modified時間戳大於請求中發送的時間戳。 這將包括它剛剛收到的對象,它用作確認記錄已成功同步到服務器。

該應用程序收到響應並執行此操作:

  • 它從JSON數組中獲取內容並修改或添加它包含的記錄。 每條記錄設置一個sync_status為0。

我希望有所幫助。 我可以互換地使用記錄和模型這個詞,但我認為你明白了。 祝你好運。


類似@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


我認為對於GUID問題的一個很好的解決方案是“分佈式ID系統”。 我不確定什麼是正確的術語,但我認為這就是MS SQL服務器文檔用來調用它的原因(SQL使用/使用此方法用於分佈式/同步數據庫)。 這很簡單:

服務器分配所有ID。 每次同步完成後,首先檢查的是“我在此客戶端上剩餘了多少個ID?” 如果客戶端運行不正常,它會向服務器請求一個新的ID塊。 客戶端然後使用該範圍內的ID作為新記錄。 如果您可以分配足夠大的塊以便在下次同步之前“永遠不會耗盡”,但不會太長以至於服務器隨時間耗盡,這對於大多數需求非常適用。 如果客戶端用完了,處理可能非常簡單,只是告訴用戶“抱歉,您不能添加更多項目,直到您同步”......如果他們添加了很多項目,他們不應該同步以避免陳舊的數據問題呢?

我認為這比使用隨機GUID更好,因為隨機GUID不是100%安全的,並且通常需要比標準ID(128位vs 32位)長得多。 你通常通過ID索引,並經常保持在內存中的ID號碼,所以重要的是要保持他們的小。

並不是真的想作為回答發布,但我不知道任何人都會將其視為評論,我認為這個主題很重要,並且不包含在其他答案中。


我這裡有一個簡單的解決方案,你可以為UIPickerView實現UIPickerViewDataSource的3個方法

    - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
    return 5;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
    switch (component) {
        case 0:
            return 24;
            break;
        case 1:
        case 3:
            return 1;
            break;
        case 2:
        case 4:
            return 60;
            break;
        default:
            return 1;
            break;
    }
}

- (NSString*)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
    switch (component) {
        case 1:
        case 3:
            return @":";
            break;
        case 0:
        case 2:
        case 4:
            return [NSString stringWithFormat:@"%lu", (long) row];
            break;
        default:
            return @"";
            break;
    }
}





iphone ios core-data sync data-synchronization