Sincronizzare offline le app per dispositivi mobili iOSEnable offline syncing with iOS mobile apps

PanoramicaOverview

Questa esercitazione illustra come eseguire la sincronizzazione offline con la funzionalità App per dispositivi mobili di Servizio app di Azure per iOS.This tutorial covers offline syncing with the Mobile Apps feature of Azure App Service for iOS. La sincronizzazione offline consente agli utenti finali di usare un'app per dispositivi mobili per visualizzare, aggiungere o modificare dati anche in assenza di una connessione di rete.With offline syncing end-users can interact with a mobile app to view, add, or modify data, even when they have no network connection. Le modifiche vengono archiviate in un database locale.Changes are stored in a local database. Quando il dispositivo viene connesso nuovamente alla rete, le modifiche vengono sincronizzate con il back-end remoto.After the device is back online, the changes are synced with the remote back end.

Se questa è la prima esperienza con la funzionalità App per dispositivi mobili, è consigliabile completare prima l'esercitazione Creare un'app iOS.If this is your first experience with Mobile Apps, you should first complete the tutorial [Create an iOS App]. Se non si usa il progetto server di avvio rapido scaricato, è necessario aggiungere al progetto i pacchetti di estensione per l'accesso ai dati.If you do not use the downloaded quick-start server project, you must add the data-access extension packages to your project. Per altre informazioni sui pacchetti di estensione server, vedere l'articolo relativo all' utilizzo dell'SDK del server back-end .NET per app per dispositivi mobili di Azure.For more information about server extension packages, see Work with the .NET backend server SDK for Azure Mobile Apps.

Per altre informazioni sulla funzionalità di sincronizzazione offline, vedere l'argomento Sincronizzazione di dati offline nelle app per dispositivi mobili.To learn more about the offline sync feature, see [Offline Data Sync in Mobile Apps].

Verificare il codice di sincronizzazione del clientReview the client sync code

Il progetto client scaricato per l'esercitazione Creare un'app iOS contiene già il codice che supporta la sincronizzazione offline mediante un database basato sui dati principali locali.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. Questa sezione riepiloga gli elementi già inclusi nel codice dell'esercitazione.This section summarizes what is already included in the tutorial code. Per una panoramica concettuale della funzionalità, vedere Sincronizzazione di dati offline nelle app per dispositivi mobili.For a conceptual overview of the feature, see [Offline Data Sync in Mobile Apps].

La funzionalità di sincronizzazione dei dati offline di App per dispositivi mobili consente agli utenti finali di interagire con un database locale quando la rete non è disponibile.Using the offline data-sync feature of Mobile Apps, end-users can interact with a local database even when the network is inaccessible. Per usare queste funzionalità nell'app, è possibile inizializzare il contesto di sincronizzazione di MSClient e fare riferimento a un archivio locale.To use these features in your app, you initialize the sync context of MSClient and reference a local store. Fare quindi riferimento alla tabella tramite l'interfaccia MSSyncTable.Then you reference your table through the MSSyncTable interface.

In QSTodoService.m (Objective-C) o ToDoTableViewController.swift (Swift) si noti che il tipo del membro syncTable è MSSyncTable.In QSTodoService.m (Objective-C) or ToDoTableViewController.swift (Swift), notice that the type of the member syncTable is MSSyncTable. La sincronizzazione offline usa questa interfaccia della tabella di sincronizzazione al posto di MSTable.Offline sync uses this sync table interface instead of MSTable. Quando si usa una tabella di sincronizzazione, tutte le operazioni vengono inviate all'archivio locale e vengono sincronizzate con il back-end remoto solo mediante operazioni push e pull esplicite.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.

Per ottenere un riferimento a una tabella di sincronizzazione, usare il metodo syncTableWithName su MSClient.To get a reference to a sync table, use the syncTableWithName method on MSClient. Per rimuovere la funzionalità di sincronizzazione offline, usare invece tableWithName.To remove offline sync functionality, use tableWithName instead.

Prima di poter eseguire qualsiasi operazione su tabella, è necessario inizializzare l'archivio locale.Before any table operations can be performed, the local store must be initialized. Di seguito è riportato il codice pertinente.Here is the relevant code:

  • Objective-C:Objective-C. Nel metodo 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. Nel metodo 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)
    

    Questo metodo crea un archivio locale usando l'interfaccia MSCoreDataStore, disponibile in Mobile Apps SDK.This method creates a local store by using the MSCoreDataStore interface, which the Mobile Apps SDK provides. È anche possibile fornire un archivio locale differente, implementando il protocollo MSSyncContextDataSource.Alternatively, you can provide a different local store by implementing the MSSyncContextDataSource protocol. Il primo parametro di MSSyncContext viene usato per specificare un gestore di conflitti.Also, the first parameter of MSSyncContext is used to specify a conflict handler. Poiché è stato passato nil, si otterrà il gestore di conflitti predefinito, che non consente l'esecuzione di operazioni in caso di conflitto.Because we have passed nil, we get the default conflict handler, which fails on any conflict.

A questo punto, si esegue l'operazione effettiva di sincronizzazione e si ottengono i dati dal back-end remoto.Now, let's perform the actual sync operation, and get data from the remote back end:

  • Objective-C:Objective-C. syncData effettua innanzitutto il push delle nuove modifiche e quindi chiama il metodo pullData per ottenere i dati dal back-end remoto.syncData first pushes new changes and then calls pullData to get data from the remote back end. A sua volta, il metodo pullData ottiene nuovi dati che corrispondono a una query: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()
       }
    }
    

Nella versione Objective-C, in syncData, viene innanzitutto chiamato il metodo pushWithCompletion nel contesto di sincronizzazione.In the Objective-C version, in syncData, we first call pushWithCompletion on the sync context. Questo metodo fa parte di MSSyncContext (e non della tabella di sincronizzazione) perché effettua il push delle modifiche in tutte le tabelle.This method is a member of MSSyncContext (and not the sync table itself) because it pushes changes across all tables. Solo i record che sono stati in qualche modo modificati localmente (tramite le operazioni CUD) vengono inviati al server.Only records that have been modified in some way locally (through CUD operations) are sent to the server. Viene quindi chiamato l'helper pullData, che chiama MSSyncTable.pullWithQuery per recuperare i dati remoti e memorizzarli nel database locale.Then the helper pullData is called, which calls MSSyncTable.pullWithQuery to retrieve remote data and store it in the local database.

Nella versione Swift, poiché l'operazione push non è strettamente necessaria, non vi è alcuna chiamata a pushWithCompletion.In the Swift version, because the push operation was not strictly necessary, there is no call to pushWithCompletion. Se nel contesto di sincronizzazione per la tabella che esegue un'operazione push sono presenti modifiche in sospeso, pull effettua sempre prima un'operazione push.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. Tuttavia, se sono presenti più tabelle di sincronizzazione, è preferibile chiamare in modo esplicito il push per garantire la coerenza tra le tabelle correlate.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.

Sia nella versione Objective-C che nella versione Swift, è possibile usare il metodo pullWithQuery per specificare una query per filtrare i record da recuperare.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. In questo esempio, la query recupera tutti i record nella tabella TodoItem remota.In this example, the query retrieves all records in the remote TodoItem table.

Il secondo parametro di pullWithQuery è un ID di query che viene usato per la sincronizzazione incrementale. La sincronizzazione incrementale recupera solo i record che sono stati modificati dopo l'ultima sincronizzazione, usando il timestamp UpdatedAt del record denominato updatedAt nell'archivio locale. L'ID di query deve essere una stringa descrittiva univoca per ogni query logica presente nell'app.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. Per rifiutare esplicitamente la sincronizzazione incrementale, passare nil come ID di query.To opt out of incremental sync, pass nil as the query ID. Questo approccio può potenzialmente non essere efficiente, perché recupera tutti i record ad ogni operazione pull.This approach can be potentially inefficient, because it retrieves all records on each pull operation.

L'app Objective-C esegue la sincronizzazione quando si modificano o si aggiungono dati, quando un utente esegue l'aggiornamento e all'avvio.The Objective-C app syncs when you modify or add data, when a user performs the refresh gesture, and on launch.

L'app Swift esegue la sincronizzazione quando l'utente esegue l'aggiornamento e all'avvio.The Swift app syncs when the user performs the refresh gesture and on launch.

Poiché l'app esegue la sincronizzazione ogni volta che i dati vengono modificati (Objective-C) oppure a ogni avvio dell'applicazione (Objective-C e Swift), l'app presuppone che l'utente sia online.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. In un'altra sezione, l'app verrà aggiornata in modo che gli utenti possano apportare modifiche anche quando sono offline.In a later section, you will update the app so that users can edit even when they are offline.

Esaminare il modello di Core DataReview the Core Data model

Quando si usa l'archivio offline Core Data, è necessario definire particolari tabelle e campi all'interno del modello di dati.When you use the Core Data offline store, you must define particular tables and fields in your data model. L'app di esempio include già un modello di dati nel formato corretto.The sample app already includes a data model with the right format. Questa sezione illustra le tabelle e il relativo uso.In this section, we walk through these tables to show how they are used.

Aprire QSDataModel.xcdatamodeld.Open QSDataModel.xcdatamodeld. Qui sono definite quattro tabelle, tre usate dall'SDK e una per gli elementi attività:Four tables are defined--three that are used by the SDK and one that's used for the to-do items themselves:

  • MS_TableOperations: tiene traccia degli elementi da sincronizzare con il server.MS_TableOperations: Tracks the items that need to be synchronized with the server.
  • MS_TableOperationErrors: tiene traccia di eventuali errori che si verificano durante la sincronizzazione offline.MS_TableOperationErrors: Tracks any errors that happen during offline synchronization.
  • MS_TableConfig: tiene traccia dell'ora dell'ultimo aggiornamento dell'ultima operazione di sincronizzazione per tutte le operazioni pull.MS_TableConfig: Tracks the last updated time for the last sync operation for all pull operations.
  • TodoItem: archivia gli elementi attività.TodoItem: Stores the to-do items. Le colonne di sistema createdAt, updatedAt e version sono proprietà di sistema facoltative.The system columns createdAt, updatedAt, and version are optional system properties.

Nota

Mobile Apps SDK si riserva i nomi di colonna che iniziano con "``".The Mobile Apps SDK reserves column names that begin with "``". Usare questo prefisso solo per le colonne di sistema.Do not use this prefix with anything other than system columns. In caso contrario, quando si usa il back-end remoto, i nomi di colonna vengono modificati.Otherwise, your column names are modified when you use the remote back end.

Quando si usa la funzionalità di sincronizzazione offline, definire le tre tabelle di sistema e la tabella dati.When you use the offline sync feature, define the three system tables and the data table.

Tabelle di sistemaSystem tables

MS_TableOperationsMS_TableOperations

Attributi della tabella MS_TableOperations

AttributoAttribute typeType
idid Valore integer 64Integer 64
itemIditemId stringString
propertiesproperties Dati binariBinary Data
tabellatable stringString
tableKindtableKind Integer 16Integer 16

MS_TableOperationErrorsMS_TableOperationErrors

Attributi della tabella MS_TableOperationErrors

AttributoAttribute typeType
idid stringString
operationIdoperationId Valore integer 64Integer 64
propertiesproperties Dati binariBinary Data
tableKindtableKind Integer 16Integer 16

MS_TableConfigMS_TableConfig

AttributoAttribute typeType
idid stringString
keykey stringString
keyTypekeyType Valore integer 64Integer 64
tabellatable stringString
valuevalue stringString

Tabella datiData table

TodoItemTodoItem

AttributoAttribute typeType NoteNote
idid Stringa, contrassegnata come obbligatoriaString, marked required chiave primaria nell'archivio remotoPrimary key in remote store
completecomplete booleanBoolean campo elemento ToDoTo-do item field
texttext stringString campo elemento ToDoTo-do item field
createdAtcreatedAt DataDate (facoltativo) viene mappato alla proprietà di sistema createdAt(optional) Maps to createdAt system property
updatedAtupdatedAt DataDate (facoltativo) viene mappato alla proprietà di sistema updatedAt(optional) Maps to updatedAt system property
versionversion stringString (facoltativo) viene usato per il rilevamento dei conflitti, viene mappato a version(optional) Used to detect conflicts, maps to version

Modificare il comportamento di sincronizzazione dell'appChange the sync behavior of the app

In questa sezione si modifica l'app in modo che non esegua la sincronizzazione all'avvio o quando si inseriscono e si aggiornano elementi,In this section, you modify the app so that it does not sync on app start or when you insert and update items. bensì solo quando si seleziona il pulsante di aggiornamento.It syncs only when the refresh gesture button is performed.

Objective-C:Objective-C:

  1. In QSTodoListViewController.m modificare il metodo viewDidLoad per rimuovere la chiamata a [self refresh] alla fine del metodo.In QSTodoListViewController.m, change the viewDidLoad method to remove the call to [self refresh] at the end of the method. A questo punto i dati non vengono sincronizzati con il server all'avvio dell'app.Now the data is not synced with the server on app start. Sono invece sincronizzati con il contenuto dell'archivio locale.Instead, it's synced with the contents of the local store.
  2. In QSTodoService.m modificare la definizione di addItem in modo che non esegua la sincronizzazione dopo l'inserimento dell'elemento.In QSTodoService.m, modify the definition of addItem so that it doesn't sync after the item is inserted. Rimuovere il blocco self syncData e sostituirlo con quanto segue:Remove the self syncData block and replace it with the following:

    if (completion != nil) {
        dispatch_async(dispatch_get_main_queue(), completion);
    }
    
  3. Modificare la definizione di completeItem come indicato in precedenza.Modify the definition of completeItem as mentioned previously. Rimuovere il blocco per self syncData e sostituirlo con il codice seguente:Remove the block for self syncData and replace it with the following: objc if (completion != nil) { dispatch_async(dispatch_get_main_queue(), completion); }

Swift:Swift:

In viewDidLoad in ToDoTableViewController.swift impostare un commento per queste due righe per interrompere la sincronizzazione all'avvio dell'app.In viewDidLoad, in ToDoTableViewController.swift, comment out the two lines shown here, to stop syncing on app start. Al momento della stesura di questo articolo, l'app Swift Todo non aggiorna il servizio quando un utente aggiunge 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. Aggiorna il servizio solo all'avvio.It updates the service only on app start.

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

Test dell'appTest the app

In questa sezione, ci si collega a un URL non valido per simulare uno scenario offline.In this section, you connect to an invalid URL to simulate an offline scenario. Quando si aggiungono elementi di dati, questi vengono conservati nell'archivio Core Data locale, ma non vengono sincronizzati con il back-end dell'app per dispositivi mobili.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. Modificare l'URL dell'app per dispositivi mobili in QSTodoService.m con un URL non valido ed eseguire di nuovo l'app:Change the mobile-app URL in QSTodoService.m to an invalid URL, and run the app again:

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

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

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

    let client = MSClient(applicationURLString: "https://sitename.azurewebsites.net.fail")
    
  2. Aggiungere alcuni elementi attività.Add some to-do items. Uscire dal simulatore (o forzare la chiusura dell'app) e riavviare.Quit the simulator (or forcibly close the app), and then restart it. Verificare che le modifiche siano state conservate.Verify that your changes persist.

  3. Visualizzare il contenuto della tabella TodoItem remota:View the contents of the remote TodoItem table:

    • Per un back-end Node.js, passare al portale di Azure e nel back-end dell'app per dispositivi mobili fare clic su Tabelle semplici > TodoItem.For a Node.js back end, go to the Azure portal and, in your mobile-app back end, click Easy Tables > TodoItem.
    • Per il back-end .NET, usare uno strumento SQL, quale ad esempio SQL Server Management Studio, oppure un client REST, quale ad esempio 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. Verificare che i nuovi elementi non siano stati sincronizzati con il server.Verify that the new items have not been synced with the server.

  5. Ripristinare l'URL corretto in QSTodoService.m ed eseguire di nuovo l'applicazione.Change the URL back to the correct one in QSTodoService.m, and rerun the app.

  6. Eseguire il movimento di aggiornamento spostando verso il basso l'elenco di elementi.Perform the refresh gesture by pulling down the list of items.
    Verrà visualizzato un indicatore di avanzamento.A progress spinner is displayed.

  7. Visualizzare nuovamente i dati di TodoItem.View the TodoItem data again. Gli elementi attività nuovi e modificati dovrebbero essere a questo punto visualizzati.The new and changed to-do items should now be displayed.

SummarySummary

Per supportare la funzionalità di sincronizzazione offline è stata usata l'interfaccia MSSyncTable ed è stato inizializzato MSClient.syncContext con un archivio locale.To support the offline sync feature, we used the MSSyncTable interface and initialized MSClient.syncContext with a local store. In questo caso l'archivio locale era un database basato su Core Data.In this case, the local store was a Core Data-based database.

Quando si usa un archivio locale Core Data, è necessario definire varie tabelle con le proprietà di sistema corrette.When you use a Core Data local store, you must define several tables with the correct system properties.

Le normali operazioni CRUD (create, read, update, delete) per le app per dispositivi mobili funzionano come se l'app fosse connessa alla rete, ma tutte le operazioni si verificano nell'archivio locale.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.

Quando l'archivio locale è stato sincronizzato con il server, è stato usato il metodo MSSyncTable.pullWithQuery.When we synchronized the local store with the server, we used the MSSyncTable.pullWithQuery method.

Risorse aggiuntiveAdditional resources