2012-08-14 19 views
8

Aquí está mi problema:¿Sincroniza los datos centrales de IOS con el servicio web?

  • Quiero usar los datos de la base - los problemas de velocidad y conectividad para construir mi IOS aplicación. Los datos almacenados en los datos centrales provienen de una base de datos SQLServer a la que puedo acceder a través de un servicio web aún por definir.
  • Cualquier cambio en los datos almacenados en los datos centrales debe sincronizarse con el SQLServer a través de un servicio web. Además, necesito almacenar en el búfer los cambios que no se sincronizan debido a problemas de conectividad.
  • También necesito actualizar los datos principales con cualquier cambio que haya ocurrido en el servidor. Esto podría suceder en un horario establecido en las preferencias del usuario.

Soluciones He explorado:

  • Usando NSIncrementalStore clase (nuevo en IOS 5). Estoy muy confundido sobre lo que hace exactamente esto, pero suena prometedor. Por lo que puedo decir, subclase NSIncrementalStore que le permite interceptar las llamadas regulares de la API de datos centrales. Podría pasar la información a los datos centrales y sincronizarla con la base de datos externa a través de un servicio web. Podría estar completamente equivocado. Pero suponiendo que estoy en lo cierto, ¿cómo sincronizaría los deltas si la conexión a Internet no funciona?
  • AFIncrementalStore - Esta es una subclase de NSIncrementalStore que usa AFNetworking para hacer la pieza de servicios web.
  • RestKit - Estoy un poco preocupado sobre cuán activa es esta API y parece estar pasando por una transición a la funcionalidad de bloqueo. ¿Alguien ha usado esto extensamente?

Me estoy inclinando hacia AFIncrementalStore ya que esto está utilizando (lo que parece ser) un enfoque más estándar. El problema es que podría estar completamente apagado en lo que realmente es NSIncrementalStore.

¡Un enlace a un ejemplo de código o tutorial sería genial!

Respuesta

5

Mi solución a esto fue almacenar dos copias del conjunto de datos en una base de datos CoreData. Uno representa el último estado del servidor conocido y es inmutable. El otro es editado por el usuario.

Cuando llega el momento de sincronizar los cambios, la aplicación crea una diferencia entre las copias editadas e inmutables de los datos. La aplicación envía el diff a un servicio web que aplica el diff a su propia copia de los datos. Responde con una copia completa del conjunto de datos, que la aplicación sobrescribe en ambas copias de los datos.

Las ventajas son:

  • Si no hay conectividad de red, no se pierden los cambios: el diff se calcula cada vez que el conjunto de datos tiene que ser enviado, y la copia inmutable sólo se modifique por una exitosa sincronizar
  • Solo se transmite la cantidad mínima de información que se debe enviar.
  • Varias personas pueden editar los mismos datos al mismo tiempo sin utilizar estrategias de bloqueo con una oportunidad mínima para la pérdida de datos mediante sobrescrituras.

Las desventajas son:

  • escribir el código diffing es compleja.
  • Escribir el servicio de fusión es complejo.
  • A menos que sea un gurú de la metaprogramación, encontrará que su código diff/merge es frágil y tiene que cambiar cada vez que cambie su modelo de objeto.

Estas son algunas de las consideraciones que tenía cuando sube con la estrategia:

  • Si permite que se realicen cambios sin conexión, cierre Entrada/Salida no funcionará (¿cómo se puede establecer una bloqueo sin conexión?).
  • ¿Qué sucede si dos personas editan los mismos datos al mismo tiempo?
  • ¿Qué sucede si una persona edita datos en un dispositivo iOS cuando no tiene conexión, lo apaga, lo edita en otro dispositivo y luego vuelve a encenderlo?
  • El subprocesamiento múltiple con CoreData es una clase de problema completo en sí mismo.

Lo más parecido que he oído hablar de que el apoyo fuera de la caja para hacer algo remotamente parecido a esto es el nuevo sistema de sincronización iCloud/CoreData en iOS 6, que transmite automáticamente entidades de una base de datos CoreData a iCloud cuando cambian Sin embargo, eso significa que debes usar iCloud.

EDITAR: Esto es muy tarde, lo sé, pero aquí hay una clase que es capaz de producir una diferencia entre dos instancias de 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 

Hay más información sobre lo que hace, cómo lo hace y sus limitaciones/supuestos aquí:

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

+0

Creo que la funcionalidad de sincronización de la que está hablando está disponible en IOS5 pero el show-stopper (como usted citó) es que se está sincronizando con iCloud y no con el sistema con el que debo interactuar (SQLServer). Me gusta mucho tu idea, pero me preocupan los problemas que señalaste: descifrar los deltas. ¿Tienes alguna experiencia con NSIncrementalStore? Todavía estoy confundido sobre lo que es. – JustLearningAgain

+0

NSIncrementalStore es una clase base para implementar cualquier tipo de sistema de almacenamiento como una tienda CoreData. ¿Desea usar un archivo XML con la API CoreData? Heredar de NSIncrementalStore y escribir los métodos para hacerlo. No creo que te ayude con tu situación. – Ant

+0

¿Me permite hacer ambas cosas? ¿Podría usarlo para actualizar los datos principales, así como mi servicio web externo? – JustLearningAgain

0

uso de la plataforma Analizar y su IOS SDK para estructurar y almacenar información. Puede almacenar datos en caché localmente para que pueda recuperarlos rápidamente y cuando no haya conectividad.

Cuestiones relacionadas