iOS モバイル アプリでオフライン同期を有効にするEnable offline syncing with iOS mobile apps

注意

Visual Studio App Center では、モバイル アプリ開発の中心となる新しい統合サービスに投資しています。Visual Studio App Center is investing in new and integrated services central to mobile app development. 開発者は、ビルドテスト配布のサービスを使用して、継続的インテグレーションおよびデリバリー パイプラインを設定できます。Developers can use Build, Test and Distribute services to set up Continuous Integration and Delivery pipeline. アプリがデプロイされたら、開発者は分析および診断のサービスを利用してアプリの状態と使用状況を監視し、プッシュ サービスを利用してユーザーと関わることができます。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. また、開発者は Auth を利用してユーザーを認証し、データ サービスを利用してクラウド内のアプリ データを保持および同期することもできます。Developers can also leverage Auth to authenticate their users and Data service to persist and sync app data in the cloud. App Center を今すぐチェックしてください。Check out App Center today.

概要Overview

このチュートリアルでは、iOS 向け Azure App Service Mobile Apps 機能を使用したオフライン同期について説明します。This tutorial covers offline syncing with the Mobile Apps feature of Azure App Service for iOS. オフライン同期により、エンド ユーザーはネットワークに接続していなくても、モバイル アプリを操作してデータを表示、追加、または変更できます。With offline syncing end-users can interact with a mobile app to view, add, or modify data, even when they have no network connection. 変更は、ローカル データベースに格納されます。Changes are stored in a local database. デバイスが再びオンラインになると、変更がリモート バックエンドと同期されます。After the device is back online, the changes are synced with the remote back end.

Mobile Apps を初めて使用する場合は、最初に「iOS アプリの作成」チュートリアルを完了してください。If this is your first experience with Mobile Apps, you should first complete the tutorial Create an iOS App. ダウンロードしたクイックスタート サーバー プロジェクトを使用しない場合は、データ アクセス拡張機能パッケージをプロジェクトに追加する必要があります。If you do not use the downloaded quick-start server project, you must add the data-access extension packages to your project. サーバーの拡張機能パッケージの詳細については、「 Work with the .NET backend server SDK for Azure Mobile Apps (Azure Mobile Apps 用の .NET バックエンド サーバー SDK を操作する)」を参照してください。For more information about server extension packages, see Work with the .NET backend server SDK for Azure Mobile Apps.

オフライン同期機能の詳細については、Mobile Apps でのオフライン データ同期に関する記事をご覧ください。To learn more about the offline sync feature, see Offline Data Sync in Mobile Apps.

クライアント同期コードの確認Review the client sync code

iOS アプリの作成」チュートリアルでダウンロードしたクライアント プロジェクトには、ローカルの 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. このセクションでは、チュートリアルのコードに既に含まれているものについて簡単に説明します。This section summarizes what is already included in the tutorial code. 機能の概念的な概要については、Mobile Apps でのオフライン データ同期に関する記事をご覧ください。For a conceptual overview of the feature, see Offline Data Sync in Mobile Apps.

Mobile Apps のオフライン データ同期機能を使用すると、エンド ユーザーは、ネットワークにアクセスできない場合でもローカル データベースを操作できます。Using the offline data-sync feature of Mobile Apps, end-users can interact with a local database even when the network is inaccessible. アプリケーションでこれらの機能を使用するには、 MSClient の同期コンテキストを初期化して、ローカル ストアを参照します。To use these features in your app, you initialize the sync context of MSClient and reference a local store. 次に、MSSyncTable インターフェイスを使用してテーブルを参照します。Then you reference your table through the MSSyncTable interface.

QSTodoService.m (Objective-C) または ToDoTableViewController.swift (Swift) では、メンバー syncTable の型は MSSyncTable です。In QSTodoService.m (Objective-C) or ToDoTableViewController.swift (Swift), notice that the type of the member syncTable is MSSyncTable. オフライン同期では、MSTable の代わりにこの同期テーブル インターフェイスを使用します。Offline sync uses this sync table interface instead of MSTable. 同期テーブルを使用すると、すべての操作はローカル ストアを参照し、明示的なプッシュ操作とプル操作を使用してリモート バックエンドとのみ同期されます。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.

同期テーブルへの参照を取得するには、MSClientsyncTableWithName メソッドを使用します。To get a reference to a sync table, use the syncTableWithName method on MSClient. オフライン同期機能を削除するには、tableWithName を使用します。To remove offline sync functionality, use tableWithName instead.

テーブル操作を実行する前に、ローカル ストアを初期化する必要があります。Before any table operations can be performed, the local store must be initialized. 関連するコードを次に示します。Here is the relevant code:

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

    このメソッドでは、Mobile Apps SDK で提供される MSCoreDataStore インターフェイスを使用してローカル ストアを作成します。This method creates a local store by using the MSCoreDataStore interface, which the Mobile Apps SDK provides. MSSyncContextDataSource プロトコルを実装することで、別のローカル ストアを提供することもできます。Alternatively, you can provide a different local store by implementing the MSSyncContextDataSource protocol. また、MSSyncContext の最初のパラメーターは、競合ハンドラーを指定するために使用します。Also, the first parameter of MSSyncContext is used to specify a conflict handler. ここでは nil を渡したため、既定の競合ハンドラーが取得されます。この競合ハンドラーは、競合が発生すると失敗します。Because we have passed nil, we get the default conflict handler, which fails on any conflict.

では、実際の同期操作を実行し、リモート バックエンドからデータを取得してみましょう。Now, let's perform the actual sync operation, and get data from the remote back end:

  • Objective-C:Objective-C. syncData で新しい変更をプッシュしてから pullData を呼び出すことで、リモート バックエンドからデータを取得します。syncData first pushes new changes and then calls pullData to get data from the remote back end. 次に、pullData メソッドがクエリに一致する新しいデータを取得します。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()
        }
    }
    

Objective C バージョンでは、syncData で同期コンテキストの最初に pushWithCompletion を呼び出しています。In the Objective-C version, in syncData, we first call pushWithCompletion on the sync context. このメソッドは、すべてのテーブルに対して変更をプッシュするため、(同期テーブル自体ではなく) MSSyncContext のメンバーです。This method is a member of MSSyncContext (and not the sync table itself) because it pushes changes across all tables. (CUD 操作によって) 何らかの方法でローカルで変更されたレコードだけがサーバーに送信されます。Only records that have been modified in some way locally (through CUD operations) are sent to the server. 次に、ヘルパー pullData が呼び出されます。このヘルパーが MSSyncTable.pullWithQuery を呼び出してリモート データを取得し、ローカル データベースに格納します。Then the helper pullData is called, which calls MSSyncTable.pullWithQuery to retrieve remote data and store it in the local database.

Swift バージョンでは、プッシュ操作は必ずしも必要ではなかったため、pushWithCompletion の呼び出しはありません。In the Swift version, because the push operation was not strictly necessary, there is no call to pushWithCompletion. プッシュ操作を行っているテーブルの同期コンテキストで保留中の変更がある場合は、プルは必ず最初にプッシュを発行します。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. ただし、複数の同期テーブルを使用している場合は、関連するテーブル全体で整合性を確保するために、プッシュを明示的に呼び出すことをお勧めします。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.

Objective-C と Swift のどちらのバージョンでも、pullWithQuery メソッドを使用して、取得するレコードをフィルター処理するクエリを指定できます。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. この例のクエリでは、リモートの TodoItem テーブルのすべてのレコードを取得します。In this example, the query retrieves all records in the remote TodoItem table.

pullWithQuery の 2 番目のパラメーターは、増分同期に使用するクエリ ID です。増分同期では、レコードの UpdatedAt タイムスタンプ (ローカル ストアでは updatedAt と呼ばれます) を使用して、前回の同期以降に変更されたレコードだけを取得します。クエリ ID は、アプリ内の各論理クエリに対して一意の、わかりやすい文字列にする必要があります。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. 増分同期を解除するには、クエリ ID として nil を渡します。To opt out of incremental sync, pass nil as the query ID. この方法では、プル操作のたびにすべてのレコードを取得するため、非効率的になる可能性があります。This approach can be potentially inefficient, because it retrieves all records on each pull operation.

Objective-C のアプリは、データを変更または追加したとき、ユーザーが更新操作を実行したとき、および起動時に同期します。The Objective-C app syncs when you modify or add data, when a user performs the refresh gesture, and on launch.

Swift のアプリは、ユーザーが更新操作を実行したときと起動時に同期します。The Swift app syncs when the user performs the refresh gesture and on launch.

アプリはデータが変更されたとき (Objective-C) またはアプリの起動時 (Objective-C と Swift) に同期するため、ユーザーがオンラインであることを前提としています。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 a later section, you will update the app so that users can edit even when they are offline.

Core Data モデルの確認Review the Core Data model

Core Data オフライン ストアを使用するときは、データ モデルで特定のテーブルとフィールドを定義する必要があります。When you use the Core Data offline store, you must define particular tables and fields in your data model. サンプル アプリケーションには、あらかじめ適切な形式でデータ モデルが含まれています。The sample app already includes a data model with the right format. このセクションでは、これらのテーブルとその使用方法について説明します。In this section, we walk through these tables to show how they are used.

QSDataModel.xcdatamodeld を開きます。Open QSDataModel.xcdatamodeld. 4 つのテーブルが定義されています。3 つは SDK で使用され、1 つは To Do 項目自体に使用されます。Four tables are defined--three that are used by the SDK and one that's used for the to-do items themselves:

  • MS_TableOperations:サーバーと同期する必要がある項目を追跡します。MS_TableOperations: Tracks the items that need to be synchronized with the server.
  • MS_TableOperationErrors:オフライン同期中に発生したエラーを追跡します。MS_TableOperationErrors: Tracks any errors that happen during offline synchronization.
  • MS_TableConfig:すべてのプル操作に対する最後の同期操作の最終更新時刻を追跡します。MS_TableConfig: Tracks the last updated time for the last sync operation for all pull operations.
  • TodoItem:To Do アイテムを格納します。TodoItem: Stores the to-do items. システム列 createdAtupdatedAt、および version は省略可能なシステム プロパティです。The system columns createdAt, updatedAt, and version are optional system properties.

注意

Mobile Apps SDK では、" `` " で始まる列名を予約しています。The Mobile Apps SDK reserves column names that begin with "``". このプレフィックスは、システム列以外のものに使用しないでください。Do not use this prefix with anything other than system columns. そうしないと、リモート バックエンドの使用時に列名が変更されます。Otherwise, your column names are modified when you use the remote back end.

オフライン同期機能を使用するときは、3 つのシステム テーブルと、データ テーブルを定義します。When you use the offline sync feature, define the three system tables and the data table.

システム テーブルSystem tables

MS_TableOperationsMS_TableOperations

MS_TableOperations テーブルの属性

AttributeAttribute TypeType
idid Integer 64Integer 64
itemIditemId stringString
propertiesproperties Binary DataBinary Data
tabletable stringString
tableKindtableKind Integer 16Integer 16

MS_TableOperationErrorsMS_TableOperationErrors

MS_TableOperationErrors テーブルの属性

AttributeAttribute TypeType
idid stringString
operationIdoperationId Integer 64Integer 64
propertiesproperties Binary DataBinary Data
tableKindtableKind Integer 16Integer 16

MS_TableConfigMS_TableConfig

AttributeAttribute TypeType
idid stringString
keykey stringString
keyTypekeyType Integer 64Integer 64
tabletable stringString
valuevalue stringString

データ テーブルData table

TodoItemTodoItem

AttributeAttribute TypeType NoteNote
idid String、必須のマークString, marked required リモート ストア内のプライマリ キーPrimary key in remote store
completecomplete BooleanBoolean To Do 項目フィールドTo-do item field
texttext stringString To Do 項目フィールドTo-do item field
createdAtcreatedAt DateDate (省略可能) createdAt システム プロパティにマップします。(optional) Maps to createdAt system property
updatedAtupdatedAt DateDate (省略可能) updatedAt システム プロパティにマップします。(optional) Maps to updatedAt system property
versionversion stringString (省略可能) 競合の検出に使用され、バージョンにマップします。(optional) Used to detect conflicts, maps to version

アプリケーションの同期動作を変更するChange the sync behavior of the app

このセクションでは、アプリの起動時または項目を挿入および更新したときに同期しないように、アプリを変更します。In this section, you modify the app so that it does not sync on app start or when you insert and update items. 更新操作ボタンが実行されたときにのみ同期します。It syncs only when the refresh gesture button is performed.

Objective-C:Objective-C:

  1. QSTodoListViewController.mviewDidLoad メソッドを変更して、メソッドの最後にある [self refresh] への呼び出しを削除します。In QSTodoListViewController.m, change the viewDidLoad method to remove the call to [self refresh] at the end of the method. これで、アプリの起動時にデータはサーバーと同期されなくなります。Now the data is not synced with the server on app start. 代わりに、ローカル ストアの内容と同期されます。Instead, it's synced with the contents of the local store.

  2. QSTodoService.maddItem の定義を変更して、項目の挿入後に同期しないようにします。In QSTodoService.m, modify the definition of addItem so that it doesn't sync after the item is inserted. self syncData ブロックを削除し、代わりに次を配置します。Remove the self syncData block and replace it with the following:

    if (completion != nil) {
        dispatch_async(dispatch_get_main_queue(), completion);
    }
    
  3. 前述のように、completeItem の定義を変更します。Modify the definition of completeItem as mentioned previously. self syncData のブロックを削除し、次のコードに置き換えます。Remove the block for self syncData and replace it with the following:

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

Swift:Swift:

viewDidLoadToDoTableViewController.swift で、次に示す 2 行をコメントアウトしてアプリの起動時に同期しないようにします。In viewDidLoad, in ToDoTableViewController.swift, comment out the two lines shown here, to stop syncing on app start. この記事の執筆時点では、Swift Todo アプリはユーザーが項目を追加または完了したときにはサービスを更新せず、At the time of this writing, the Swift Todo app does not update the service when someone adds or completes an item. アプリの起動時にのみサービスを更新しています。It updates the service only on app start.

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

アプリケーションをテストするTest the app

ここでは、無効な URL に接続してオフライン シナリオをシミュレートします。In this section, you connect to an invalid URL to simulate an offline scenario. データ項目を追加すると、ローカル Core Data ストアに保持されますが、モバイル アプリ バックエンドとは同期されません。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. QSTodoService.m のモバイル アプリ URL を無効な URL に変更し、アプリをもう一度実行します。Change the mobile-app URL in QSTodoService.m to an invalid URL, and run the app again:

    Objective-C:Objective-C. QSTodoService.m のコードを次に示します。In QSTodoService.m:

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

    Swift:Swift. ToDoTableViewController.swift のコードを次に示します。In ToDoTableViewController.swift:

    let client = MSClient(applicationURLString: "https://sitename.azurewebsites.net.fail")
    
  2. To Do 項目をいくつか追加します。Add some to-do items. シミュレーターを終了し (またはアプリを強制的に閉じて)、再起動します。Quit the simulator (or forcibly close the app), and then restart it. 変更内容が保持されていることを確認します。Verify that your changes persist.

  3. リモートの TodoItem テーブルの内容を表示します。View the contents of the remote TodoItem table:

    • Node.js バックエンドの場合は、Azure Portal に移動し、モバイル アプリ バックエンドで [簡易テーブル] > 、[TodoItem] をクリックします。For a Node.js back end, go to the Azure portal and, in your mobile-app back end, click Easy Tables > TodoItem.
    • .NET バックエンドの場合は、SQL Server Management Studio などの SQL ツール、または Fiddler や Postman などの REST クライアントを使用します。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. 新しい項目がサーバーと同期されなかったことを確認します。Verify that the new items have not been synced with the server.

  5. QSTodoService.m の URL を正しい URL に変更し、アプリを再実行します。Change the URL back to the correct one in QSTodoService.m, and rerun the app.

  6. 項目の一覧をプルダウンして更新操作を実行します。Perform the refresh gesture by pulling down the list of items.
    進行状況を示すスピナーが表示されます。A progress spinner is displayed.

  7. TodoItem データをもう一度表示します。View the TodoItem data again. 今回は、新しい To Do 項目と変更された To Do 項目が表示されます。The new and changed to-do items should now be displayed.

まとめSummary

オフライン同期機能をサポートするために、MSSyncTable インターフェイスを使用し、ローカル ストアで MSClient.syncContext を初期化しました。To support the offline sync feature, we used the MSSyncTable interface and initialized MSClient.syncContext with a local store. この例では、ローカル ストアは Core Data に基づいたデータベースでした。In this case, the local store was a Core Data-based database.

Core Data ローカル ストアを使用するときは、正しいシステム プロパティを使用して、複数のテーブルを定義する必要があります。When you use a Core Data local store, you must define several tables with the correct system properties.

モバイル アプリでの通常の作成、読み取り、更新、削除 (CRUD) の各操作は、アプリがまだ接続されていても、すべての操作がローカル ストアに対して発生したかのように動作します。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.

ローカル ストアをサーバーと同期するときに、MSSyncTable.pullWithQuery メソッドを使用しました。When we synchronized the local store with the server, we used the MSSyncTable.pullWithQuery method.

その他のリソースAdditional resources