2012-08-14 90 views
8

這裏是我的問題:將IOS核心數據與Web服務同步?

  • 我想使用的核心數據 - 速度和連接問題,以建立我的iOS應用。存儲在覈心數據中的數據來自SQLServer數據庫,我可以通過一個尚未定義的Web服務訪問該數據庫。
  • 對存儲在覈心數據中的數據所做的任何更改都需要通過Web服務與SQLServer同步。另外,我需要緩存由於連接問題而不同步的更改。
  • 我還需要使用服務器上發生的任何更改來更新核心數據。這可能發生在用戶偏好設置的時間表上。

解決方案我已經瞭解:

  • 使用NSIncrementalStore類(在iOS 5中新)。我很困惑這是什麼,但聽起來很有希望。從我所知道的,你可以子類NSIncrementalStore,它允許你攔截常規的核心數據API調用。然後,我可以將這些信息傳遞給核心數據,並通過Web服務將其與外部數據庫同步。我可能完全錯誤。但假設我是對的,如果與互聯網的連接斷開,我將如何同步增量?
  • AFIncrementalStore - 這是NSIncrementalStore的子類,使用AFNetworking來完成網絡服務。
  • RestKit - 我有點擔心這個API的活躍程度,它似乎正在通過過渡到塊功能。有沒有人廣泛使用過?

我傾向於AFIncrementalStore因爲這是使用(似乎是)更標準的方法。問題是,我可能完全不知道NSIncrementalStore究竟是什麼。

一些示例代碼或教程的鏈接將會很棒!

回答

5

我對此的解決方案是將數據集的兩個副本存儲在CoreData數據庫中。一個代表最後一個已知的服務器狀態,並且是不可變的。另一個是由用戶編輯的。

當需要同步更改時,應用程序會在已編輯和不可變的數據副本之間創建差異。應用程序將diff發送到一個Web服務,該服務將diff應用於其自己的數據副本。它用數據集的完整副本進行回覆,該應用程序將覆蓋該數據的兩個副本。

的優點是:

  • 如果沒有網絡連接,沒有任何更改將丟失:中DIFF在每個數據集需要被髮送的時間計算,並且不可改變的副本只改變了一個成功的同步。
  • 只發送需要發送的最小信息量。
  • 多人可以同時編輯相同的數據,而無需使用鎖定策略,並通過覆蓋來減少數據丟失的機會。

缺點是:

  • 編寫代碼版本比較複雜。
  • 編寫合併服務很複雜。
  • 除非您是元編程專家,否則您會發現您的差異/合併代碼很脆弱,並且每當您更改對象模型時都必須更改。

這裏有一些上來時與策略,我有考慮的:

  • 如果允許更改離線製作,簽入/籤鎖定將無法正常工作(你怎麼能建立一個鎖定沒有連接?)。
  • 如果兩個人同時編輯相同的數據會發生什麼?
  • 如果一個人在無連接時在一臺iOS設備上編輯數據,將其關閉,在另一臺設備上編輯然後重新打開原始設備,會發生什麼情況?
  • CoreData多線程本身就是一個完整的問題類。

我聽說到外的現成支持做任何遠程這樣最接近的是iOS6的,它會自動從數據庫CoreData到iCloud傳送實體新的iCloud/CoreData同步系統當他們改變時。但是,這意味着您必須使用iCloud。

編輯:這是非常晚,我知道,但是這裏有一個類能夠產生兩個NSManagedObject實例之間的差異。

// SZManagedObjectDiff.h 
@interface SZManagedObjectDiff 

- (NSDictionary *)diffNewObject:(NSManagedObject *)newObject withOldObject:(NSManagedObject *)oldObject 

@end 

// SZManagedObjectDiff.m 
#import "SZManagedObjectDiff.h" 

@implementation SZManagedObjectDiff 

- (NSDictionary *)diffNewObject:(NSManagedObject *)newObject withOldObject:(NSManagedObject *)oldObject { 

    NSDictionary *attributeDiff = [self diffAttributesOfNewObject:newObject withOldObject:oldObject]; 

    NSDictionary *relationshipsDiff = [self diffRelationshipsOfNewObject:newObject withOldObject:oldObject]; 

    NSMutableDictionary *diff = [NSMutableDictionary dictionary]; 

    if (attributeDiff.count > 0) { 
     diff[@"attributes"] = attributeDiff; 
    } 

    if (relationshipsDiff.count > 0) { 
     diff[@"relationships"] = relationshipsDiff; 
    } 

    if (diff.count > 0) { 
     diff[@"entityName"] = newObject ? newObject.entity.name : oldObject.entity.name; 

     NSString *idAttributeName = newObject ? newObject.entity.userInfo[@"id"] : oldObject.entity.userInfo[@"id"]; 

     if (idAttributeName) { 
      id itemId = newObject ? [newObject valueForKey:idAttributeName] : [oldObject valueForKey:idAttributeName]; 

      if (itemId) { 
       diff[idAttributeName] = itemId; 
      } 
     } 
    } 

    return diff; 
} 

- (NSDictionary *)diffRelationshipsOfNewObject:(NSManagedObject *)newObject withOldObject:(NSManagedObject *)oldObject { 

    NSMutableDictionary *diff = [NSMutableDictionary dictionary]; 

    NSDictionary *relationships = newObject == nil ? [[oldObject entity] relationshipsByName] : [[newObject entity] relationshipsByName]; 

    for (NSString *name in relationships) { 

     NSRelationshipDescription *relationship = relationships[name]; 

     if (relationship.deleteRule != NSCascadeDeleteRule) continue; 

     SEL selector = NSSelectorFromString(name); 

     id newValue = nil; 
     id oldValue = nil; 

     if (newObject != nil && [newObject respondsToSelector:selector]) newValue = [newObject performSelector:selector]; 
     if (oldObject != nil && [oldObject respondsToSelector:selector]) oldValue = [oldObject performSelector:selector]; 

     if (relationship.isToMany) { 

      NSArray *changes = [self diffNewSet:newValue withOldSet:oldValue]; 

      if (changes.count > 0) { 
       diff[name] = changes; 
      } 

     } else { 

      NSDictionary *relationshipDiff = [self diffNewObject:newValue withOldObject:oldValue]; 

      if (relationshipDiff.count > 0) { 
       diff[name] = relationshipDiff; 
      } 
     } 
    } 

    return diff; 
} 

- (NSDictionary *)diffAttributesOfNewObject:(NSManagedObject *)newObject withOldObject:(NSManagedObject *)oldObject { 

    NSMutableDictionary *diff = [NSMutableDictionary dictionary]; 

    NSArray *attributeNames = newObject == nil ? [[[oldObject entity] attributesByName] allKeys] : [[[newObject entity] attributesByName] allKeys]; 

    for (NSString *name in attributeNames) { 

     SEL selector = NSSelectorFromString(name); 

     id newValue = nil; 
     id oldValue = nil; 

     if (newObject != nil && [newObject respondsToSelector:selector]) newValue = [newObject performSelector:selector]; 
     if (oldObject != nil && [oldObject respondsToSelector:selector]) oldValue = [oldObject performSelector:selector]; 

     newValue = newValue ? newValue : [NSNull null]; 
     oldValue = oldValue ? oldValue : [NSNull null]; 

     if (![newValue isEqual:oldValue]) { 
      diff[name] = @{ @"new": newValue, @"old": oldValue }; 
     } 
    } 

    return diff; 
} 

- (NSArray *)diffNewSet:(NSSet *)newSet withOldSet:(NSSet *)oldSet { 

    NSMutableArray *changes = [NSMutableArray array]; 

    // Find all items that have been newly created or updated. 
    for (NSManagedObject *newItem in newSet) { 

     NSString *idAttributeName = newItem.entity.userInfo[@"id"]; 

     NSAssert(idAttributeName, @"Entities must have an id property set in their user info."); 

     id newItemId = [newItem valueForKey:idAttributeName]; 

     NSManagedObject *oldItem = nil; 

     for (NSManagedObject *setItem in oldSet) { 
      id setItemId = [setItem valueForKey:idAttributeName]; 

      if ([setItemId isEqual:newItemId]) { 
       oldItem = setItem; 
       break; 
      } 
     } 

     NSDictionary *diff = [self diffNewObject:newItem withOldObject:oldItem]; 

     if (diff.count > 0) { 
      [changes addObject:diff]; 
     } 
    } 

    // Find all items that have been deleted. 
    for (NSManagedObject *oldItem in oldSet) { 

     NSString *idAttributeName = oldItem.entity.userInfo[@"id"]; 

     NSAssert(idAttributeName, @"Entities must have an id property set in their user info."); 

     id oldItemId = [oldItem valueForKey:idAttributeName]; 

     NSManagedObject *newItem = nil; 

     for (NSManagedObject *setItem in newSet) { 
      id setItemId = [setItem valueForKey:idAttributeName]; 

      if ([setItemId isEqual:oldItemId]) { 
       newItem = setItem; 
       break; 
      } 
     } 

     if (!newItem) { 
      NSDictionary *diff = [self diffNewObject:newItem withOldObject:oldItem]; 

      if (diff.count > 0) { 
       [changes addObject:diff]; 
      } 
     } 
    } 

    return changes; 
} 

@end 

有一個關於它做什麼,它是如何做它和它的侷限性/假設這裏的更多信息:

http://simianzombie.com/?p=2379

+0

我認爲你正在討論的同步功能可以在IOS5中找到,但是show-stopper(正如你所引用的)是它與iCloud同步,而不是我必須與之交互的系統(SQLServer)。我很喜歡你的想法,但我很關心你指出的問題:找出三角洲。你有使用NSIncrementalStore的經驗嗎?我仍然對它究竟是什麼感到困惑。 – JustLearningAgain 2012-08-15 02:23:40

+0

NSIncrementalStore是用於將任何類型的存儲系統實現爲CoreData存儲的基類。想用CoreData API使用XML文件嗎?從NSIncrementalStore繼承並編寫方法來執行此操作。我認爲這不會對你的情況有所幫助。 – Ant 2012-08-15 03:21:05

+0

它允許我做到兩個嗎?我可以用它來更新核心數據以及我的外部Web服務嗎? – JustLearningAgain 2012-08-15 07:11:39

0

使用parse平臺和IOS SDK構建和存儲信息。它可以在本地緩存數據,以便在沒有連接時快速檢索數據。