Skip to content

Files

167 lines (107 loc) · 10.3 KB

app-service-mobile-xamarin-ios-get-started-offline-data-preview.md

File metadata and controls

167 lines (107 loc) · 10.3 KB

Enable offline sync for your Xamarin iOS mobile app

[AZURE.INCLUDE app-service-mobile-selector-offline-preview]

This tutorial covers the offline sync feature of Mobile Apps for iOS. Offline sync allows end-users to interact with a mobile app--viewing, adding, or modifying data--even when there is no network connection. Changes are stored in a local database; once the device is back online, these changes are synced with the remote service.

Offline sync has several potential uses:

  • Improve app responsiveness by caching server data locally on the device
  • Make apps resilient against intermittent network connectivity
  • Allow end-users to create and modify data even when there is no network access, supporting scenarios with little or no connectivity
  • Sync data across multiple devices and detect conflicts when the same record is modified by two devices

If this is your first experience with Mobile Apps, first complete the tutorial Create a Xamarin iOS app.

This tutorial requires the following:

Review the Mobile App sync code

Mobile App offline sync allows end users to interact with a local database when the network is not accessible. To use these features in your app, you initialize MobileServiceClient.SyncContext to a local store. Then reference your table through the IMobileServiceSyncTable interface. This section walks through the offline sync related code in QSTodoService.cs.

  1. In Visual Studio, open the project that you completed in the [Get Started with Mobile Apps] tutorial. Open the file QSTodoService.cs.

  2. Notice the type of the member todoTable is IMobileServiceSyncTable. Offline sync uses this sync table interface instead of IMobileServiceTable. When a sync table is used, all operations go to the local store and are only synchronized with the remote service with explicit push and pull operations.

    To get a reference to a sync table, the method GetSyncTable() is used. To remove the offline sync functionality, you would instead use GetTable().

  3. Before any table operations can be performed, the local store must be initialized. This is done in the InitializeStoreAsync method:

     public async Task InitializeStoreAsync()
     {
         var store = new MobileServiceSQLiteStore(localDbPath);
         store.DefineTable<ToDoItem>();
    
         // Uses the default conflict handler, which fails on conflict
         await client.SyncContext.InitializeAsync(store);
     }
    

    This creates a local store using the class MobileServiceSQLiteStore, which is provided in the Mobile App SDK. You can also a provide a different local store implementation by implementing IMobileServiceLocalStore.

    The DefineTable method creates a table in the local store that matches the fields in the provided type, ToDoItem in this case. The type doesn't have to include all of the columns that are in the remote database--it is possible to store just a subset of columns.

  1. The method SyncAsync triggers the actual sync operation:

     public async Task SyncAsync()
     {
         try
         {
             await client.SyncContext.PushAsync();
             await todoTable.PullAsync("allTodoItems", todoTable.CreateQuery()); // query ID is used for incremental sync
         }
    
         catch (MobileServiceInvalidOperationException e)
         {
             Console.Error.WriteLine(@"Sync Failed: {0}", e.Message);
         }
     }
    

    First, there is a call to IMobileServiceSyncContext.PushAsync(). This method is a member of IMobileServicesSyncContext instead of the sync table because it will push changes across all tables. Only records that have been modified in some way locally (through CUD operations) will be sent to the server.

    Next, the method calls IMobileServiceSyncTable.PullAsync() to pull data from a table on the server to the app. Note that if there are any changes pending in the sync context, a pull always issues a push first. This is to ensure all tables in the local store along with relationships are consistent. In this case, we have called push explicitly.

    In this example, we retrieve all records in the remote TodoItem table, but it is also possible to filter records by passing a query. The first parameter to PullAsync() is a query ID that is used for incremental sync, which uses the UpdatedAt timestamp to get only those records modified since the last sync. The query ID should be a descriptive string that is unique for each logical query in your app. To opt-out of incremental sync, pass null as the query ID. This will retrieve all records on each pull operation, which is potentially inefficient.

  1. In the class QSTodoService, the method SyncAsync() is called after the operations that modify data, InsertTodoItemAsync() and CompleteItemAsync. It is also called from RefreshDataAsync(), so that the user gets the latest data whenever they perform the refresh gesture. The app also performs a sync on launch, since QSTodoListViewController.ViewDidLoad() calls RefreshDataAsync().

    Because SyncAsync() is called whenever data is modified, this app assumes that the user is online whenever they are editing data. In the next section, we will update the app so that users can edit even when they are offline.

Update the sync behavior of the app

In this section, you will modify the app so that it does not sync on app launch or on the insert and update operations, but only when the refresh gesture is performed. Then, you will break the app connection with the mobile backend to simulate an offline scenario. When you add data items, they will be held in the local store, but not immediately synced to the mobile backend data store.

  1. Open QSTodoService.cs. Comment out the calls to SyncAsync() in the following methods:

    • InsertTodoItemAsync
    • CompleteItemAsync
    • RefreshAsync

    Now, RefreshAsync() will only load data from the local store, but will not connect to the app backend.

  2. In QSTodoService.cs, change the definition of applicationURL to point to an invalid mobile app URI:

     const string applicationURL = @"https://your-service.azurewebsites.xxx/"; // invalid URI
    
  3. To ensure that data is synchronized when the refresh gesture is performed, edit the method QSTodoListViewController.RefreshAsync(). Add a call to SyncAsync() before the call to RefreshDataAsync():

     private async Task RefreshAsync ()
     {
         RefreshControl.BeginRefreshing ();
    
         await todoService.SyncAsync();
         await todoService.RefreshDataAsync (); // add this line
    
         RefreshControl.EndRefreshing ();
    
         TableView.ReloadData ();
     }
    
  4. Build and run the app. Add some new todo items. These new items exist only in the local store until they can be pushed to the mobile backend. The client app behaves as if is connected to the backend, supporting all create, read, update, delete (CRUD) operations.

  5. Close the app and restart it to verify that the new items you created are persisted to the local store.

Update the app to reconnect your mobile backend

In this section you will reconnect the app to the mobile backend, which simulates the app coming back to an online state. When you perform the refresh gesture, data will be synced to your mobile backend.

  1. Open QSTodoService.cs. Remove the invalid mobile app URL and add back the correct URL and app key.

  2. Rebuild and run the app. Notice that the data has not changed, even though the app is now connected to the mobile backend. This is because this app always uses the IMobileServiceSyncTable that is pointed to the local store.

  3. Connect to your backend SQL database to view the data that has been stored. In Visual Studio go to Server Explorer -> Azure -> SQL Databases. Right click your database and select Open in SQL Server Object Explorer.

    Notice the data has not been synchronized between the database and the local store.

  4. In the app, perform the refresh gesture by pulling down the list of items. This causes the app to call RefreshDataAsync(), which in turn calls SyncAsync(). This will perform the push and pull operations, first sending the local store items to the mobile backend, then retrieving new data from the backend.

  5. Refresh your database view and confirm that changes have been synchronized.