¿Cómo sincronizar iPhone Core Data con el servidor web y luego enviarlo a otros dispositivos?

He estado trabajando en un método para sincronizar datos centrales almacenados en una aplicación de iPhone entre varios dispositivos, como un iPad o una Mac. No hay muchos (si es que hay ninguno) frameworks de sincronización para usar con Core Data en iOS. Sin embargo, he estado pensando en el siguiente concepto:

  1. Se realiza un cambio en el almacén de datos central local y se guarda el cambio. (a) Si el dispositivo está en línea, intenta enviar el conjunto de cambios al servidor, incluida la ID del dispositivo que envió el conjunto de cambios. (b) Si el conjunto de cambios no llega al servidor, o si el dispositivo no está en línea, la aplicación agregará el conjunto de cambios a una cola para enviar cuando se conecte.
  2. El servidor, sentado en la nube, combina los conjuntos de cambios específicos que recibe con su base de datos maestra.
  3. Después de que un conjunto de cambios (o una cola de conjuntos de cambios) se fusiona en el servidor de la nube, el servidor empuja todos esos conjuntos de cambios a los otros dispositivos registrados en el servidor utilizando algún tipo de sistema de sondeo. (Pensé usar los servicios Push de Apple, pero aparentemente de acuerdo con los comentarios, este no es un sistema viable).

¿Hay algo elegante en lo que deba estar pensando? He analizado frameworks REST como ObjectiveResource , Core Resource y RestfulCoreData . Por supuesto, todos están trabajando con Ruby on Rails, a lo que no estoy vinculado, pero es un buen lugar para comenzar. Los principales requisitos que tengo para mi solución son:

  1. Cualquier cambio debe enviarse en segundo plano sin detener el hilo principal.
  2. Debe usar el menor ancho de banda posible.

He pensado en una serie de desafíos:

  1. Asegurarse de que las ID de los objetos para los diferentes almacenes de datos en diferentes dispositivos estén conectados al servidor. Es decir, tendré una tabla de ID de objeto e ID de dispositivo, que están vinculados mediante una referencia al objeto almacenado en la base de datos. Tendré un registro (DatabaseId [exclusivo para esta tabla], ObjectId [único para el elemento en la base de datos], Datafield1, Datafield2), el campo ObjectId hará referencia a otra tabla, AllObjects: (ObjectId, DeviceId, DeviceObjectId). Luego, cuando el dispositivo empuja hacia arriba un conjunto de cambios, pasará el ID del dispositivo y el Id del objeto desde el objeto de datos del núcleo en el almacén de datos local. Entonces, mi servidor en la nube verificará el ID del objeto y el Id. Del dispositivo en la tabla AllObjects, y buscará el registro para cambiar en la tabla inicial.
  2. Todos los cambios deben tener una marca de tiempo, para que puedan fusionarse.
  3. El dispositivo tendrá que sondear el servidor, sin consumir demasiada batería.
  4. Los dispositivos locales también necesitarán actualizar todo lo que se tenga en la memoria si / cuando se reciben cambios del servidor.

¿Hay algo más que me falta aquí? ¿Qué tipo de marcos debería considerar para que esto sea posible?

Sugiero leer detenidamente e implementar la estrategia de sincronización discutida por Dan Grover en la conferencia iPhone 2009, disponible aquí como documento pdf.

Esta es una solución viable y no es tan difícil de implementar (Dan lo implementó en varias de sus aplicaciones), superponiéndose a la solución descrita por Chris. Para una discusión teórica y en profundidad sobre la sincronización, vea el documento de Russ Cox (MIT) y William Josephson (Princeton):

Sincronización de archivos con pares de tiempo de vectores

que se aplica igualmente bien a los datos básicos con algunas modificaciones obvias. Esto proporciona una estrategia de sincronización general mucho más sólida y confiable, pero requiere más esfuerzo para implementarse correctamente.

EDITAR:

Parece que el archivo pdf de Grover ya no está disponible (enlace roto, marzo de 2015). ACTUALIZACIÓN: el enlace está disponible a través de Way Back Machine aquí

El marco de Objective-C llamado ZSync y desarrollado por Marcus Zarra ha quedado obsoleto, dado que iCloud finalmente parece ser compatible con la correcta sincronización de datos centrales.

He hecho algo similar a lo que intentas hacer. Déjame decirte lo que he aprendido y cómo lo hice.

Supongo que tiene una relación uno-a-uno entre su objeto Core Data y el modelo (o esquema db) en el servidor. Simplemente desea mantener los contenidos del servidor sincronizados con los clientes, pero los clientes también pueden modificar y agregar datos. Si entendí bien, entonces sigue leyendo.

Agregué cuatro campos para ayudar con la sincronización:

  1. sync_status : agregue este campo únicamente a su modelo de datos básicos. La aplicación lo usa para determinar si tiene un cambio pendiente en el artículo. Utilizo los siguientes códigos: 0 significa que no hay cambios, 1 significa que está en cola para sincronizarse con el servidor, y 2 significa que es un objeto temporal y se puede purgar.
  2. is_deleted : agregue esto al servidor y al modelo de datos básicos. El evento de eliminación en realidad no debería eliminar una fila de la base de datos o de su modelo de cliente porque no le deja nada para sincronizar. Al tener este indicador booleano simple, puede configurar is_deleted a 1, sincronizarlo y todos estarán contentos. También debe modificar el código en el servidor y el cliente para consultar elementos no eliminados con “is_deleted = 0”.
  3. last_modified : agregue esto al servidor y al modelo de datos básicos. Este campo debe ser actualizado automáticamente por el servidor con la fecha y hora actual cada vez que algo cambie en ese registro. Nunca debe ser modificado por el cliente.
  4. guid : agregue un id. único global (consulte http://en.wikipedia.org/wiki/Globally_unique_identifier ) al servidor y al modelo de datos principal. Este campo se convierte en la clave principal y se vuelve importante al crear nuevos registros en el cliente. Normalmente, su clave principal es un número entero creciente en el servidor, pero debemos tener en cuenta que el contenido podría crearse fuera de línea y sincronizarse más adelante. El GUID nos permite crear una clave sin estar conectado.

En el cliente, agregue código para establecer sync_status en 1 en su objeto modelo cada vez que algo cambie y deba sincronizarse con el servidor. Los objetos de modelo nuevo deben generar un GUID.

La sincronización es una única solicitud. La solicitud contiene:

  • La marca de tiempo last_modified MAX de los objetos de su modelo. Esto le dice al servidor que solo desea cambios después de esta marca de tiempo.
  • Una matriz JSON que contiene todos los elementos con sync_status = 1.

El servidor recibe la solicitud y hace esto:

  • Toma los contenidos de la matriz JSON y modifica o agrega los registros que contiene. El campo last_modified se actualiza automáticamente.
  • El servidor devuelve una matriz JSON que contiene todos los objetos con una marca de tiempo last_modified mayor que la marca de tiempo enviada en la solicitud. Esto incluirá los objetos que acaba de recibir, lo que sirve como reconocimiento de que el registro se sincronizó correctamente con el servidor.

La aplicación recibe la respuesta y hace esto:

  • Toma los contenidos de la matriz JSON y modifica o agrega los registros que contiene. Cada registro se establece en sync_status de 0.

Espero que eso ayude. Utilicé la palabra registro y modelo indistintamente, pero creo que entiendes la idea. Buena suerte.

Si aún está buscando un camino por recorrer, mire en el móvil de Couchbase. Esto básicamente hace todo lo que quieres. ( http://www.couchbase.com/nosql-databases/couchbase-mobile )

Similar a @Cris Implementé la clase para sincronización entre cliente y servidor y resolví todos los problemas conocidos hasta el momento (envío / recepción de datos hacia / desde el servidor, fusión de conflictos según sellos de tiempo, eliminación de entradas duplicadas en condiciones de red no confiables, sincronización de datos nesteds y archivos, etc.)

Simplemente diga a la clase qué entidad y qué columnas debe sincronizar y dónde está su servidor.

 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]; 

Puede encontrar la fuente, el ejemplo de trabajo y más instrucciones aquí: github.com/knagode/M3Synchronization .

Aviso al usuario para actualizar los datos a través de la notificación de inserción. Use un hilo de fondo en la aplicación para verificar los datos locales y los datos en el servidor de la nube, mientras el cambio ocurre en el servidor, cambie los datos locales, viceversa.

Así que creo que la parte más difícil es estimar los datos en qué lado se invalida.

Espero que esto te ayude

Acabo de publicar la primera versión de mi nueva API de sincronización de Core Data Cloud, conocida como SynCloud. SynCloud tiene muchas diferencias con iCloud porque permite la interfaz de sincronización multiusuario. También es diferente de otras api de sincronización porque permite datos relacionales de múltiples tablas.

Por favor, encuentre más información en http://www.syncloudapi.com

Comstackr con iOS 6 SDK, está muy actualizado a partir del 27/9/2012.

Creo que una buena solución para el problema GUID es “sistema de identificación distribuido”. No estoy seguro de cuál es el término correcto, pero creo que eso es lo que los documentos de MS SQL Server usaban para llamarlo (SQL usa / usó este método para bases de datos distribuidas / sincronizadas). Es bastante simple:

El servidor asigna todos los ID. Cada vez que se realiza una sincronización, lo primero que se marca son “¿Cuántas identificaciones me quedan en este cliente?” Si el cliente se está agotando, solicita al servidor un nuevo bloque de ID. El cliente luego usa ID en ese rango para nuevos registros. Esto funciona muy bien para la mayoría de las necesidades, si puede asignar un bloque lo suficientemente grande como para que “nunca” se agote antes de la próxima sincronización, pero no tan grande como para que el servidor se agote con el tiempo. Si el cliente alguna vez se agota, el manejo puede ser bastante simple, solo dígale al usuario “lo siento, no puede agregar más elementos hasta que se sincronice” … si están agregando tantos elementos, ¿no deberían sincronizarse para evitar datos obsoletos? problemas de todos modos?

Creo que esto es superior al uso de GUID aleatorios porque los GUID aleatorios no son 100% seguros, y generalmente necesitan ser mucho más largos que una ID estándar (128 bits frente a 32 bits). Por lo general, tiene índices por ID y, a menudo, mantiene los números de identificación en la memoria, por lo que es importante mantenerlos pequeños.

Realmente no quería publicar como respuesta, pero no sé si alguien lo verá como un comentario, y creo que es importante para este tema y no está incluido en otras respuestas.

Primero debe reconsiderar cuántos datos, tablas y relaciones tendrá. En mi solución, implementé la sincronización a través de los archivos de Dropbox. Observo los cambios en el MOC principal y guardo estos datos en los archivos (cada fila se guarda como gzip json). Si hay una conexión a Internet funcionando, verifico si hay algún cambio en Dropbox (Dropbox me da cambios delta), los descargo y fusiono (últimas ganancias), y finalmente pongo los archivos cambiados. Antes de la sincronización, coloqué el archivo de locking en Dropbox para evitar que otros clientes sincronicen datos incompletos. Al descargar cambios, es seguro que solo se descarguen datos parciales (por ejemplo, conexión a Internet perdida). Cuando finalice la descarga (total o parcial), comenzará a cargar archivos en Core Data. Cuando hay relaciones no resueltas (no se descargan todos los archivos), deja de cargar archivos e intenta finalizar la descarga más tarde. Las relaciones se almacenan solo como GUID, por lo que puedo verificar fácilmente qué archivos cargar para tener integridad de datos completa. La sincronización comienza después de realizar los cambios a los datos centrales. Si no hay cambios, comprueba los cambios en Dropbox cada pocos minutos y al inicio de la aplicación. Además, cuando se envían los cambios al servidor, envío una transmisión a otros dispositivos para informarles sobre los cambios, para que puedan sincronizarse más rápido. Cada entidad sincronizada tiene propiedad GUID (GUID también se usa como un nombre de archivo para intercambiar archivos). También tengo la base de datos Sync donde guardo la revisión de Dropbox de cada archivo (puedo compararlo cuando Dropbox delta restablece su estado). Los archivos también contienen nombre de entidad, estado (eliminado / no eliminado), guid (igual que nombre de archivo), revisión de base de datos (para detectar migraciones de datos o para evitar la sincronización con versiones nunca) y por supuesto los datos (si la fila no se elimina).

Esta solución funciona para miles de archivos y alrededor de 30 entidades. En lugar de Dropbox, podría usar el almacén de claves / valores como servicio web REST, que quiero hacer más adelante, pero no tengo tiempo para esto 🙂 Por ahora, en mi opinión, mi solución es más confiable que iCloud y, lo cual es muy importante, Tengo control total sobre cómo está funcionando (principalmente porque es mi propio código).

Otra solución es guardar los cambios de MOC como transacciones: se intercambiarán muchos menos archivos con el servidor, pero es más difícil hacer la carga inicial en el orden correcto en los datos de núcleo vacíos. iCloud funciona de esta manera, y también otras soluciones de sincronización tienen un enfoque similar, por ejemplo, TICoreDataSync .

– ACTUALIZAR

Después de un tiempo, migré a Ensembles : recomiendo esta solución sobre la reinvención de la rueda.

2017

En cuanto a esta pregunta increíblemente vieja.

Sería mucho como preguntar

“Quiero comprar un dispositivo que sea un teléfono que pueda llevar conmigo, ¡pero también lo uso para muchas tareas informáticas, incluso navegando por la WWW!”

Obviamente, la respuesta a eso es que si has estado en Marte, una de las principales tecnologías que se dieron cuenta en este planeta recientemente fue “teléfonos inteligentes”, compra uno.

Hoy en día, crear un sistema OCC desde cero sería una locura como crear una base de datos SQL desde cero.

Obviamente, para OCC, que es el paradigma básico de todas las aplicaciones no triviales ahora, usted usa

  • Firebase
  • PubNub
  • Couchbase

y así sucesivamente, que son simplemente, el mayor avance en la tecnología humana de los últimos años .

Hoy, ya no crearías OCC desde cero más de lo que harías

  • escribe tu propio sistema operativo desde cero

  • escribe tu propia base de datos SQL desde cero

  • escribe tu propia fuente-representación desde cero

Tenga en cuenta que, de hecho, en un sentido profesional ya no puede ser “un progtwigdor ios” o “un progtwigdor de Android”.

¿A quién le importa saber cómo diseñar tablas y botones?

Eres un experto en Firebase / lo que sea y, como cuestión secundaria, sabes cómo diseñar botones, etc. en iOS o Android.

El único problema es qué uso de BAAS, por ejemplo, tal vez PlayFab si está orientado al juego, tal vez PubNub si es realmente impulsado por mensajes, quizás ably.io, tal vez kinvey si es corporativo, lo que sea.