Habilitación de la sincronización sin conexión con aplicaciones móviles iOSEnable offline syncing with iOS mobile apps

Nota

Visual Studio App Center está invirtiendo en servicios nuevos e integrados que son fundamentales para el desarrollo de aplicaciones móviles.Visual Studio App Center is investing in new and integrated services central to mobile app development. Los desarrolladores pueden usar los servicios de compilación, prueba y distribución para configurar la canalización de entrega e integración continuas.Developers can use Build, Test and Distribute services to set up Continuous Integration and Delivery pipeline. Una vez que se ha implementado la aplicación, los desarrolladores pueden supervisar el estado y el uso de su aplicación con los servicios de análisis y diagnóstico, e interactuar con los usuarios mediante el servicio de inserción.Once the app is deployed, developers can monitor the status and usage of their app using the Analytics and Diagnostics services, and engage with users using the Push service. Además, los desarrolladores pueden aprovechar Auth para autenticar a los usuarios y el servicio de datos para almacenar y sincronizar los datos de la aplicación en la nube.Developers can also leverage Auth to authenticate their users and Data service to persist and sync app data in the cloud. Consulte App Center hoy mismo.Check out App Center today.

Información generalOverview

En este tutorial, se trata la sincronización sin conexión con la característica Mobile Apps de Azure App Service para iOS.This tutorial covers offline syncing with the Mobile Apps feature of Azure App Service for iOS. Con la sincronización sin conexión, los usuarios finales pueden interactuar con una aplicación móvil para ver, agregar o modificar datos, incluso si carecen de conexión a red.With offline syncing end-users can interact with a mobile app to view, add, or modify data, even when they have no network connection. Los cambios se almacenan en una base de datos local.Changes are stored in a local database. Una vez que el dispositivo se vuelve a conectar, los cambios se sincronizan con el back-end remoto.After the device is back online, the changes are synced with the remote back end.

Si esta es la primera vez que usa Mobile Apps, primero debería completar el tutorial Creación de una aplicación iOS.If this is your first experience with Mobile Apps, you should first complete the tutorial Create an iOS App. Si no usa el proyecto de servidor de inicio rápido descargado, debe agregar los paquetes de extensión de acceso de datos al proyecto.If you do not use the downloaded quick-start server project, you must add the data-access extension packages to your project. Para obtener más información acerca de los paquetes de extensión de servidor, consulte Trabajar con el SDK del servidor back-end de .NET para Aplicaciones móviles de Azure.For more information about server extension packages, see Work with the .NET backend server SDK for Azure Mobile Apps.

Para aprender acerca de la característica de sincronización sin conexión, consulte Sincronización de datos sin conexión en Mobile Apps.To learn more about the offline sync feature, see Offline Data Sync in Mobile Apps.

Revisión del código de sincronización de clienteReview the client sync code

El proyecto de cliente que descargó para el tutorial Creación de una aplicación iOS ya contiene el código que admite la sincronización sin conexión con una base de datos local basada en Core Data.The client project that you downloaded for the Create an iOS App tutorial already contains code that supports offline synchronization using a local Core Data-based database. En esta sección, se resume lo que ya está incluido en el código del tutorial.This section summarizes what is already included in the tutorial code. Para ver información general conceptual sobre la característica, consulte Sincronización de datos sin conexión en Mobile Apps.For a conceptual overview of the feature, see Offline Data Sync in Mobile Apps.

Mediante la característica de sincronización de datos sin conexión de Mobile Apps, los usuarios finales pueden interactuar con una base de datos local incluso si la red no está accesible.Using the offline data-sync feature of Mobile Apps, end-users can interact with a local database even when the network is inaccessible. Para utilizar estas características en su aplicación, inicialice el contexto de sincronización de MSClient y haga referencia a un almacén local.To use these features in your app, you initialize the sync context of MSClient and reference a local store. Después, haga referencia a la tabla mediante la interfaz MSSyncTable.Then you reference your table through the MSSyncTable interface.

En QSTodoService.m (Objective-C) o ToDoTableViewController.swift (Swift), tenga en cuenta que el tipo del miembro syncTable es MSSyncTable.In QSTodoService.m (Objective-C) or ToDoTableViewController.swift (Swift), notice that the type of the member syncTable is MSSyncTable. La sincronización sin conexión usa esta interfaz de tabla de sincronización en lugar de MSTable.Offline sync uses this sync table interface instead of MSTable. Cuando se usa una tabla de sincronización, todas las operaciones van al almacén local y solo se sincronizan con el back-end remoto con operaciones de inserción y extracción explícitas.When a sync table is used, all operations go to the local store and are synchronized only with the remote back end with explicit push and pull operations.

Para obtener una referencia a una tabla de sincronización, utilice el método syncTableWithName en MSClient.To get a reference to a sync table, use the syncTableWithName method on MSClient. Para quitar la funcionalidad de sincronización sin conexión, use tableWithName en su lugar.To remove offline sync functionality, use tableWithName instead.

Antes de poder realizar cualquier operación de tabla, se debe inicializar el almacén local.Before any table operations can be performed, the local store must be initialized. Este es el código pertinente:Here is the relevant code:

  • Objective-C.Objective-C. En el método QSTodoService.init:In the QSTodoService.init method:

    MSCoreDataStore *store = [[MSCoreDataStore alloc] initWithManagedObjectContext:context];
    self.client.syncContext = [[MSSyncContext alloc] initWithDelegate:nil dataSource:store callback:nil];
    
  • Swift.Swift. En el método ToDoTableViewController.viewDidLoad:In the ToDoTableViewController.viewDidLoad method:

    let client = MSClient(applicationURLString: "http:// ...") // URI of the Mobile App
    let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext!
    self.store = MSCoreDataStore(managedObjectContext: managedObjectContext)
    client.syncContext = MSSyncContext(delegate: nil, dataSource: self.store, callback: nil)
    

    Este método crea un almacén local mediante la interfaz MSCoreDataStore, que se proporciona en el SDK de Mobile Apps.This method creates a local store by using the MSCoreDataStore interface, which the Mobile Apps SDK provides. Como alternativa, puede proporcionar otro almacén local mediante la implementación del protocolo MSSyncContextDataSource.Alternatively, you can provide a different local store by implementing the MSSyncContextDataSource protocol. Además, el primer parámetro de MSSyncContext se utiliza para especificar un controlador de conflictos.Also, the first parameter of MSSyncContext is used to specify a conflict handler. Puesto que se ha pasado nil, se obtiene el controlador de conflictos predeterminado, que produce un error para cualquier conflicto.Because we have passed nil, we get the default conflict handler, which fails on any conflict.

Ahora, se va a realizar la operación de sincronización en sí y se van a obtener datos desde el back-end remoto:Now, let's perform the actual sync operation, and get data from the remote back end:

  • Objective-C.Objective-C. syncData primero inserta nuevos cambios y luego llama a pullData para obtener datos desde el back-end remoto.syncData first pushes new changes and then calls pullData to get data from the remote back end. A su vez, el método pullData obtiene datos nuevos que coinciden con una consulta:In turn, the pullData method gets new data that matches a query:

    -(void)syncData:(QSCompletionBlock)completion
    {
         // Push all changes in the sync context, and then pull new data.
         [self.client.syncContext pushWithCompletion:^(NSError *error) {
             [self logErrorIfNotNil:error];
             [self pullData:completion];
         }];
    }
    
    -(void)pullData:(QSCompletionBlock)completion
    {
         MSQuery *query = [self.syncTable query];
    
         // Pulls data from the remote server into the local table.
         // We're pulling all items and filtering in the view.
         // Query ID is used for incremental sync.
         [self.syncTable pullWithQuery:query queryId:@"allTodoItems" completion:^(NSError *error) {
             [self logErrorIfNotNil:error];
    
             // Lets the caller know that we have finished.
             if (completion != nil) {
                 dispatch_async(dispatch_get_main_queue(), completion);
             }
         }];
    }
    
  • SWIFT:Swift:

    func onRefresh(sender: UIRefreshControl!) {
        UIApplication.sharedApplication().networkActivityIndicatorVisible = true
    
        self.table!.pullWithQuery(self.table?.query(), queryId: "AllRecords") {
            (error) -> Void in
    
            UIApplication.sharedApplication().networkActivityIndicatorVisible = false
    
            if error != nil {
                // A real application would handle various errors like network conditions,
                // server conflicts, etc. via the MSSyncContextDelegate
                print("Error: \(error!.description)")
    
                // We will discard our changes and keep the server's copy for simplicity
                if let opErrors = error!.userInfo[MSErrorPushResultKey] as? Array<MSTableOperationError> {
                    for opError in opErrors {
                        print("Attempted operation to item \(opError.itemId)")
                        if (opError.operation == .Insert || opError.operation == .Delete) {
                            print("Insert/Delete, failed discarding changes")
                            opError.cancelOperationAndDiscardItemWithCompletion(nil)
                        } else {
                            print("Update failed, reverting to server's copy")
                            opError.cancelOperationAndUpdateItem(opError.serverItem!, completion: nil)
                        }
                    }
                }
            }
            self.refreshControl?.endRefreshing()
        }
    }
    

En la versión Objective-C, en syncData, se llama primero a pushWithCompletion en el contexto de sincronización.In the Objective-C version, in syncData, we first call pushWithCompletion on the sync context. Este método es miembro de MSSyncContext (no de la propia tabla de sincronización), porque inserta cambios en todas las tablas.This method is a member of MSSyncContext (and not the sync table itself) because it pushes changes across all tables. Solo se envían al servidor los registros que se han modificado localmente de alguna forma (mediante operaciones CUD).Only records that have been modified in some way locally (through CUD operations) are sent to the server. A continuación, se llama a la aplicación auxiliar pullData, que llama a MSSyncTable.pullWithQuery para recuperar datos remotos y almacenarlos en la base de datos local.Then the helper pullData is called, which calls MSSyncTable.pullWithQuery to retrieve remote data and store it in the local database.

En la versión Swift, como la operación de inserción no era estrictamente necesaria, no hay ninguna llamada a pushWithCompletion.In the Swift version, because the push operation was not strictly necessary, there is no call to pushWithCompletion. Si hay cambios pendientes en el contexto de sincronización para la tabla que está realizando una operación de inserción, la extracción siempre realiza primero una inserción.If there are any changes pending in the sync context for the table that is doing a push operation, pull always issues a push first. Sin embargo, si tiene más de una tabla de sincronización, es mejor llamar explícitamente a la inserción para asegurarse de que todo sea coherente en las tablas relacionadas.However, if you have more than one sync table, it is best to explicitly call push to ensure that everything is consistent across related tables.

Tanto en la versión Objective-C como en la Swift, puede usar el método pullWithQuery para especificar una consulta para filtrar los registros que desea recuperar.In both the Objective-C and Swift versions, you can use the pullWithQuery method to specify a query to filter the records you want to retrieve. En este ejemplo, la consulta recupera todos los registros en la tabla TodoItem remota.In this example, the query retrieves all records in the remote TodoItem table.

El segundo parámetro de pullWithQuery es un identificador de consulta que se utiliza para la sincronización incremental. La sincronización incremental recupera solo los registros que se modificaron desde la última sincronización, haciendo uso de la marca de tiempo UpdatedAt del registro (llamada updatedAt en el almacén local). El identificador de la consulta debe ser una cadena descriptiva que sea única para cada consulta lógica en la aplicación.The second parameter of pullWithQuery is a query ID that is used for incremental sync. Incremental sync retrieves only records that were modified since the last sync, using the record's UpdatedAt time stamp (called updatedAt in the local store.) The query ID should be a descriptive string that is unique for each logical query in your app. Para desactivar la sincronización incremental, pase nil como identificador de la consulta.To opt out of incremental sync, pass nil as the query ID. Es posible que este enfoque resulte ineficaz, ya que recupera todos los registros en cada operación de extracción.This approach can be potentially inefficient, because it retrieves all records on each pull operation.

La aplicación Objective-C se sincroniza cuando se modifican o agregan datos, cuando un usuario realiza el gesto de actualización y en el inicio.The Objective-C app syncs when you modify or add data, when a user performs the refresh gesture, and on launch.

La aplicación Swift se sincroniza cuando el usuario realiza el gesto de actualización y en el inicio.The Swift app syncs when the user performs the refresh gesture and on launch.

Dado que la aplicación se sincroniza cada vez que se modifican datos (Objective-C) o siempre que se inicia la aplicación (Objective-C y Swift), la aplicación supone que el usuario está en línea.Because the app syncs whenever data is modified (Objective-C) or whenever the app starts (Objective-C and Swift), the app assumes that the user is online. En una sección posterior, actualizará la aplicación para que los usuarios puedan editar incluso cuando estén sin conexión.In a later section, you will update the app so that users can edit even when they are offline.

Revisión del modelo Core DataReview the Core Data model

Cuando use el almacén sin conexión Core Data, debe definir tablas y campos determinados en el modelo de datos.When you use the Core Data offline store, you must define particular tables and fields in your data model. La aplicación de ejemplo ya incluye un modelo de datos con el formato correcto.The sample app already includes a data model with the right format. En esta sección se le guiará por estas tablas y el modo de utilizarlas.In this section, we walk through these tables to show how they are used.

Abra QSDataModel.xcdatamodeld.Open QSDataModel.xcdatamodeld. Hay cuatro tablas definidas: tres usadas por el SDK y una para las tareas pendientes en sí:Four tables are defined--three that are used by the SDK and one that's used for the to-do items themselves:

  • MS_TableOperations: realiza el seguimiento de los elementos que deben sincronizarse con el servidor.MS_TableOperations: Tracks the items that need to be synchronized with the server.
  • MS_TableOperationErrors: realiza el seguimiento de los errores que se producen durante la sincronización sin conexión.MS_TableOperationErrors: Tracks any errors that happen during offline synchronization.
  • MS_TableConfig: realiza el seguimiento de la hora de la última actualización de la última operación de sincronización para todas las operaciones de extracción.MS_TableConfig: Tracks the last updated time for the last sync operation for all pull operations.
  • TodoItem: almacena las tareas pendientes.TodoItem: Stores the to-do items. Las columnas del sistema createdAt, updatedAt y version son propiedades del sistema opcionales.The system columns createdAt, updatedAt, and version are optional system properties.

Nota

El SDK de Mobile Apps tiene reservados los nombres de columna que empiezan por " `` ".The Mobile Apps SDK reserves column names that begin with "``". Utilice este prefijo exclusivamente para las columnas del sistema.Do not use this prefix with anything other than system columns. De lo contrario, se modifican los nombres de las columnas cuando se usa el back-end remoto.Otherwise, your column names are modified when you use the remote back end.

Cuando use la característica de sincronización sin conexión, defina las tres tablas del sistema y la tabla de datos.When you use the offline sync feature, define the three system tables and the data table.

Tablas del sistemaSystem tables

MS_TableOperationsMS_TableOperations

Atributos de la tabla MS_TableOperations

AtributoAttribute typeType
idid Integer 64Integer 64
itemIditemId StringString
propertiesproperties Binary DataBinary Data
tabletable StringString
tableKindtableKind Integer 16Integer 16

MS_TableOperationErrorsMS_TableOperationErrors

Atributos de la tabla MS_TableOperationErrors

AtributoAttribute typeType
idid StringString
operationIdoperationId Integer 64Integer 64
propertiesproperties Binary DataBinary Data
tableKindtableKind Integer 16Integer 16

MS_TableConfigMS_TableConfig

AtributoAttribute typeType
idid StringString
keykey StringString
keyTypekeyType Integer 64Integer 64
tabletable StringString
valuevalue StringString

Tabla de datosData table

TodoItemTodoItem

AtributoAttribute typeType Nota:Note
idid String, marcado obligatorioString, marked required Clave principal en almacén remotoPrimary key in remote store
completecomplete BooleanBoolean Campo de tarea pendienteTo-do item field
texttext StringString Campo de tarea pendienteTo-do item field
createdAtcreatedAt DateDate (opcional) Se asigna a la propiedad del sistema createdAt(optional) Maps to createdAt system property
updatedAtupdatedAt DateDate (opcional) Se asigna a la propiedad del sistema updatedAt(optional) Maps to updatedAt system property
versionversion StringString (opcional) Se usa para detectar conflictos; se asigna a version(optional) Used to detect conflicts, maps to version

Cambio del comportamiento de sincronización de la aplicaciónChange the sync behavior of the app

En esta sección, modifique la aplicación para que no se sincronice al iniciarse la aplicación ni cuando se inserten y actualicen elementos.In this section, you modify the app so that it does not sync on app start or when you insert and update items. Solo se sincroniza cuando se realiza el gesto de actualización.It syncs only when the refresh gesture button is performed.

Objective-C:Objective-C:

  1. En QSTodoListViewController.m, cambie el método viewDidLoad para quitar la llamada a [self refresh] al final del método.In QSTodoListViewController.m, change the viewDidLoad method to remove the call to [self refresh] at the end of the method. Ahora los datos no se han sincronizado con el servidor al iniciarse la aplicación.Now the data is not synced with the server on app start. En su lugar, se sincronizan con el contenido del almacén local.Instead, it's synced with the contents of the local store.

  2. En QSTodoService.m, modifique la definición de addItem para que no se sincronice tras la inserción del elemento.In QSTodoService.m, modify the definition of addItem so that it doesn't sync after the item is inserted. Quite el bloque self syncData y sustitúyalo por lo siguiente:Remove the self syncData block and replace it with the following:

    if (completion != nil) {
        dispatch_async(dispatch_get_main_queue(), completion);
    }
    
  3. Modifique la definición de completeItem como se mencionó antes.Modify the definition of completeItem as mentioned previously. Quite el bloque para self syncData y sustitúyalo por lo siguiente:Remove the block for self syncData and replace it with the following:

    if (completion != nil) {
        dispatch_async(dispatch_get_main_queue(), completion);
    }
    

SWIFT:Swift:

En viewDidLoad, en ToDoTableViewController.swift, convierta en comentario las dos líneas que se muestran aquí para detener la sincronización al iniciarse la aplicación.In viewDidLoad, in ToDoTableViewController.swift, comment out the two lines shown here, to stop syncing on app start. En el momento de redactar este artículo, la aplicación Swift Todo no actualiza el servicio cuando alguien agrega o completa un elemento.At the time of this writing, the Swift Todo app does not update the service when someone adds or completes an item. Solo lo actualiza en el inicio de aplicación.It updates the service only on app start.

self.refreshControl?.beginRefreshing()
self.onRefresh(self.refreshControl)

Prueba de la aplicaciónTest the app

En esta sección, se conecta a una dirección URL no válida para simular un escenario sin conexión.In this section, you connect to an invalid URL to simulate an offline scenario. Cuando agrega elementos de datos, se guardan en el almacén local Core Data, pero no se sincronizan con el back-end de la aplicación móvil.When you add data items, they're held in the local Core Data store, but they're not synced with the mobile-app back end.

  1. Cambie la dirección URL de la aplicación móvil en QSTodoService.m por una no válida y vuelva a ejecutar la aplicación:Change the mobile-app URL in QSTodoService.m to an invalid URL, and run the app again:

    Objective-C.Objective-C. En QSTodoService.m:In QSTodoService.m:

    self.client = [MSClient clientWithApplicationURLString:@"https://sitename.azurewebsites.net.fail"];
    

    Swift.Swift. En ToDoTableViewController.swift:In ToDoTableViewController.swift:

    let client = MSClient(applicationURLString: "https://sitename.azurewebsites.net.fail")
    
  2. Agregue algunas tareas pendientes.Add some to-do items. Salga del simulador (o fuerce el cierre de la aplicación) y reinícielo.Quit the simulator (or forcibly close the app), and then restart it. Compruebe que los cambios se conserven.Verify that your changes persist.

  3. Vea el contenido de la tabla TodoItem remota:View the contents of the remote TodoItem table:

    • En un back-end de Node.js, vaya a Azure Portal y, en el back-end de su aplicación móvil, haga clic en Tablas fáciles > TodoItem.For a Node.js back end, go to the Azure portal and, in your mobile-app back end, click Easy Tables > TodoItem.
    • En un back-end de .NET, use una herramienta SQL, como SQL Server Management Studio, o un cliente REST, como Fiddler o Postman.For a .NET back end, use either a SQL tool, such as SQL Server Management Studio, or a REST client, such as Fiddler or Postman.
  4. Compruebe que los nuevos elementos no se hayan sincronizado con el servidor.Verify that the new items have not been synced with the server.

  5. Vuelva a cambiar la dirección URL por la correcta en QSTodoService.m y ejecute de nuevo la aplicación.Change the URL back to the correct one in QSTodoService.m, and rerun the app.

  6. Realice el gesto de actualización desplegando la lista de elementos.Perform the refresh gesture by pulling down the list of items.
    Se muestra un control giratorio de progreso.A progress spinner is displayed.

  7. Vea los datos de TodoItem de nuevo.View the TodoItem data again. Ahora deben aparecer las tareas nuevas y modificadas.The new and changed to-do items should now be displayed.

ResumenSummary

Para admitir la característica de sincronización sin conexión, se usa la interfaz MSSyncTable y se inicializa MSClient.syncContext con un almacén local.To support the offline sync feature, we used the MSSyncTable interface and initialized MSClient.syncContext with a local store. En este caso, el almacén local era una base de datos Core Data.In this case, the local store was a Core Data-based database.

Cuando usa un almacén local Core Data, debe definir varias tablas con las propiedades del sistema correctas.When you use a Core Data local store, you must define several tables with the correct system properties.

Las operaciones normales de creación, lectura, actualización y eliminación (CRUD) para aplicaciones móviles funcionan como si la aplicación siguiera conectada, pero todas ellas se producen en el almacén local.The normal create, read, update, and delete (CRUD) operations for mobile apps work as if the app is still connected, but all the operations occur against the local store.

Cuando el almacén local se sincroniza con el servidor, se usa el método MSSyncTable.pullWithQuery.When we synchronized the local store with the server, we used the MSSyncTable.pullWithQuery method.

Recursos adicionalesAdditional resources