2012-08-14 12 views
8

Oto mój problem:Synchronizacja danych podstawowych systemu IOS z usługą sieci Web?

  • Chcę używać podstawowych danych - problemy z łącznością prędkości i zbudować swoją aplikację iOS. Dane przechowywane w danych podstawowych pochodzą z bazy danych SQLServer, do której mogę uzyskać dostęp za pomocą usługi internetowej, która nie może być jeszcze zdefiniowana.
  • Wszelkie zmiany danych przechowywanych w podstawowych danych muszą być synchronizowane z SQLServer za pośrednictwem usługi internetowej. Ponadto muszę buforować zmiany, które nie są synchronizowane z powodu problemów z łącznością.
  • Muszę również zaktualizować podstawowe dane za pomocą wszelkich zmian, które wystąpiły na serwerze. Może się to zdarzyć zgodnie z harmonogramem ustawionym w preferencjach użytkownika.

Rozwiązania jakie analizy na:

  • Korzystanie NSIncrementalStore klasę (nowy w IOS 5). Jestem bardzo zdezorientowany tym, co to dokładnie robi, ale brzmi obiecująco. Z tego, co mogę powiedzieć, masz podklasę NSIncrementalStore, która pozwala ci przechwycić standardowe wywołania interfejsu API danych podstawowych. Mogę następnie przekazać te informacje do podstawowych danych, a także zsynchronizować je z zewnętrzną bazą danych za pośrednictwem usługi internetowej. Mogłem się całkowicie mylić. Ale zakładając, że mam rację, w jaki sposób mogę zsynchronizować delty, jeśli połączenie z internetem nie działa?
  • AFIncrementalStore - Jest to podklasa wyłączona z NSIncrementalStore przy użyciu AFNetworking, aby wykonać element usług WWW.
  • - Trochę niepokoję się tym, jak aktywny jest ten interfejs API i wydaje się, że przechodzi on przez blokowanie funkcjonalności. Czy ktokolwiek z tego korzystał?

Pochylam się w kierunku AFIncrementalStore, ponieważ jest to (co wydaje się być) bardziej standardowe podejście. Problem polega na tym, że mógłbym całkowicie odejść od tego, czym naprawdę jest.

Link do przykładowego kodu lub samouczka byłby świetny!

Odpowiedz

5

Moim rozwiązaniem było przechowywanie dwóch kopii zestawu danych w bazie danych CoreData. Jeden reprezentuje ostatni znany stan serwera i jest niezmienny. Drugi jest edytowany przez użytkownika.

Gdy nadszedł czas na synchronizację zmian, aplikacja tworzy różnicę między edytowanymi i niezmiennymi kopiami danych. Aplikacja przesyła diff do usługi internetowej, która stosuje różnicę do własnej kopii danych. Odpowiada on pełnej kopii zbioru danych, który aplikacja zastępuje na obu kopiach danych.

Zaletami są:

  • Jeśli nie ma połączenia z siecią, żadne zmiany nie zostaną utracone: diff jest obliczany za każdym razem, gdy zestaw danych musi zostać wysłane i niezmienny kopia zostanie zmieniony tylko na sukces synchronizacja.
  • Wysyłana jest tylko minimalna ilość informacji, które należy wysłać.
  • Wiele osób może edytować te same dane w tym samym czasie bez użycia strategii blokowania z minimalną możliwością utraty danych przez nadpisanie.

Wadami są:

  • pisanie kodu diffing jest złożona.
  • Pisanie usługi łączenia jest złożone.
  • Jeśli nie jesteś guru metaprogramowania, przekonasz się, że kod diff/merge jest kruchy i musi się zmieniać po każdej zmianie modelu obiektu.

Oto niektóre kwestie miałem podczas wymyślanie strategii:

  • Jeśli pozwolisz wprowadzenie zmian w trybie offline, zamek zameldowania/wymeldowania nie zadziała (w jaki sposób można ustanowić zablokować bez połączenia?).
  • Co się stanie, jeśli dwie osoby edytują te same dane w tym samym czasie?
  • Co się dzieje, gdy jedna osoba edytuje dane na jednym urządzeniu z systemem iOS, gdy jest bezpołączona, wyłącza je, edytuje na innym urządzeniu, a następnie ponownie włącza oryginalne urządzenie?
  • Wielowątkowość z CoreData to sama klasa problemów.

Najbliższy rzeczą Słyszałem o wsparciu out-of-the-box nic robić zdalnie jak to jest nowy system synchronizacji iCloud/CoreData w iOS6, który automatycznie przekazuje podmiotom z bazy CoreData do iCloud kiedy się zmieniają. Oznacza to jednak, że musisz używać iCloud.

EDYCJA: Jest to bardzo późno, wiem, ale tutaj jest klasa, która jest zdolna do tworzenia diff między dwie instancje 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 

Jest więcej informacji o tym, co robi, jak to robi i swoje ograniczenia/założenia tutaj:

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

+0

Myślę, że funkcja synchronizacji, o której mówisz jest dostępna w IOS5, ale show-stopper (jak wspomniano) jest to, że jest to synchronizacja z iCloud, a nie z systemem, z którym muszę się połączyć (SQLServer). Bardzo podoba mi się twój pomysł, ale jestem zaniepokojony problemami, które wskazałeś: ustalenie delt. Czy masz jakieś doświadczenie z NSIncrementalStore? Nadal jestem zdezorientowany co to jest. – JustLearningAgain

+0

NSIncrementalStore jest klasą podstawową do implementacji dowolnego systemu pamięci masowej jako sklepu CoreData. Chcesz użyć pliku XML z interfejsem API CoreData? Odziedzicz z NSIncrementalStore i napisz metody, aby to zrobić. Nie sądzę, że to pomoże w twojej sytuacji. – Ant

+0

Czy pozwala mi to robić jedno i drugie? Czy mogę użyć go do aktualizacji podstawowych danych, a także mojej zewnętrznej usługi internetowej? – JustLearningAgain

0

Wykorzystanie platformy analizowania i jego IOS SDK struktury i informacji w sklepie. Może buforować dane lokalnie, dzięki czemu można je szybko pobrać i gdy nie ma łączności.

Powiązane problemy