Couchbase Mobile開発：バックグラウンドでのCouchbase Serverとの同期

## はじめに

ここでは、Couchbaseが公開しているCouchbasae Mobileチュートリアルから、バックグラウンドフェッチに関するものを紹介します。

https://docs.couchbase.com/userprofile-couchbase-mobile/backgroundfetch/userprofile/background-fetch.html

Couchbase Mobileについては、以下参照ください。

https://qiita.com/yoshiyuki_kono/items/6a5bbad1b02196a25417

https://techbookfest.org/product/5074954942939136?productVariantID=5042865933647872

Couchbase Mobileは、NoSQL組み込みデータベースCouchbase Lite、およびCouchbase ServerとCouchbase Liteとのデータ同期を担うSync Gatewayからなります。
Couchbase Serverについては、次の拙著を紹介させていただきます。

https://www.amazon.co.jp/-/en/%E6%B2%B3%E9%87%8E-%E6%B3%B0%E5%B9%B8-ebook/dp/B09KTRXG7X/ref=sr_1_2?crid=1M9TBPNHHS12Z&keywords=Couchbase&qid=1642992094&s=books&sprefix=couchbase%2Cstripbooks%2C285&sr=1-2

### チュートリアルについて

以下で、プロジェクトが公開されています。

https://github.com/couchbaselabs/userprofile-couchbase-mobile.git


本稿の内容は、下記チュートリアルに基づきます。

https://docs.couchbase.com/userprofile-couchbase-mobile/backgroundfetch/userprofile/background-fetch.html#introduction

本稿では、バックグラウンドでの同期に関する内容の紹介にフォーカスしています。実行環境・手順などの詳細は、上記ご確認ください。


## バックグラウンドでのCouchbase Serverとの同期

### iOS Background App Refresh（バックグラウンドアプリ更新）とは

iOSはいくつかのバックグラウンドモデルをサポートしています。そのオプションの1つに、Background App Refresh（バックグラウンドアプリ更新）があります。Background App Refreshを使用すると、アプリをバックグラウンドで定期的に実行して、コンテンツを更新できるようになります。

アプリを起動して実行するタイミングは、iOSによって決定されます。通常、時間の経過とともにアプリの使用状況が学習されます。
言い換えると、必ずしもアプリがバックグラウンドで実行される機会があるという保証はありません。機会が与えられた場合でも、アプリの実行時間には限りがあります（30秒）。

### 有効化設定


Background App Refreshを有効化するには、プロジェクトの[Capabilities 機能]タブを開きます。[バックグラウンドモード Background Modes]セクションで[バックグラウンドフェッチ Background fetch]が有効にします。

チュートリアルのUserProfileDemo.xcodeprojファイルを利用する場合は、あらかじめ設定されています。上記の設定を確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176567/ea24150c-9a5b-8f71-c1c8-5950725d41f1.png)

### 実行周期設定

AppDelegate.swiftファイルを開き、`application:didFinishLaunchingWithOptions()`関数を確認します。

```swift:AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    self.loadLoginViewController()
UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum)
    return true
}
```

ここで、アプリを起動する周期を指定しています。システムでサポートされている最小のフェッチ間隔である`UIApplicationBackgroundFetchIntervalMinimum`の値を使用しています。

### コールバックの実装
AppDelegate.swiftファイルを開き、`application:performFetchWithCompletionHandler()`関数を確認します。このコールバック関数は、システムによってアプリがバックグラウンドで起動される際に呼び出されます。

```swift:AppDelegate.swift
// Support for background fetch
func application( _ application: UIApplication,
                  performFetchWithCompletionHandler
    completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    print(#function)
    
    // Do a one shot replication
    self.cbMgr.startOneShotPullReplicationForCurrentUser { (status) in
        completionHandler(.newData) 
        
    }
    
}
```

ここでは、Sync Gatewayでワンショットプルレプリケーションを実行しています。アプリが起動されるたびに、Sync Gatewayで保留中のすべての変更が同期されます

## ワンショットレプリケーションの実装
ワンショットレプリケーション用のアプリのセットアップは、同期チュートリアルで説明した連続レプリケーションと非常によく似ています。

DatabaseManager.swiftの`startOneShotPullReplicationForCurrentUser()`関数で実装されてます。

```swift:DatabaseManager.swift
func startOneShotPullReplicationForCurrentUser(completionHandler:@escaping(_ doneStatus:Bool)->Void) {
```

レプリケーターの構成は、以下のようになっています。

```swift:DatabaseManager.swift
let dbUrl = remoteUrl.appendingPathComponent(kDBName)
let config = ReplicatorConfiguration.init(database: db, target: URLEndpoint.init(url:dbUrl))
config.continuous =  false ①
config.replicatorType = .pull ② 
config.authenticator =  BasicAuthenticator(username: user, password: password) 
```

- ①「continuous」モードが「false」に設定されており、これがワンショットレプリケーションであることを示しています。ワンショットモードでは、保留中のすべての変更が同期されると、レプリケーターはアイドル状態になります
- ② 「replicationType」は「pull」に設定されており、アプリがバックグラウンドでリモートからの変更をプルすることを示しています。


## シミュレーターによる確認

Sync Gatewayで行われた変更がバックグラウンドでiOSアプリによってどのように取得されるかを確認します。

バックグラウンドフェッチをシミュレートするためには、xcodeデバッガーを使用します。アプリは、xcodeデバッガーが接続されたシミュレーターで実行されている必要があります。

シミュレーターのアプリをバックグラウンドにプッシュします。通常、Shift+Cmd+Hの組み合わせで実行できます。

xcodeのデバッグコンソールログをクリアします。これにより、アプリがバックグラウンドで起動されたときに何が起こっているかを簡単に識別できるようになります。

コマンドターミナルを開き、次のcurlコマンドを発行して、GET Document RESTAPIを介してSync　Gatewayでユーザープロファイルドキュメントを取得します

```BASH
curl -X GET \
  http://localhost:4985/userprofile/user::demo@example.com \
  -H 'Accept: application/json' \
  -H 'Cache-Control: no-cache' \
  -H 'Content-Type: application/json'
```

応答は次のようになります。実際の内容は、アプリ上で作成されたユーザープロファイルによって異なります。

```BASH
{
  "_attachments": {
    "blob_/image": {
      "content_type": "image/jpeg",
      "digest": "sha1-9OHO0QIk+kREiOaoMlrEg/jU4zU=",
      "length": 15110,
      "revpos": 1,
      "stub": true
    }
  },
  "_id": "user::demo@example.com",
  "_rev": "1-0b9e3efa5ec1b1596564c4db34ba28d4e618f9c5",
  "address": "",
  "email": "demo@example.com",
  "image": {
    "@type": "blob",
    "content_type": "image/jpeg",
    "digest": "sha1-9OHO0QIk+kREiOaoMlrEg/jU4zU=",
    "length": 15110
  },
  "name": "",
  "type": "user"
}
```

上記のプロファイルの一部を変更するために、次のコマンドを発行して、PUT Document REST APIを介してSync Gatewayのユーザープロファイルドキュメントを更新します。

```BASH
curl -X PUT \
  'http://localhost:4985/userprofile/user::demo@example.com?rev=1-0b9e3efa5ec1b1596564c4db34ba28d4e618f9c5' \
  -H 'Accept: application/json' \
  -H 'Cache-Control: no-cache' \
  -H 'Content-Type: application/json' \
  -d '{
  "address": "101 Main Street",
  "email": "demo@example.com",
  "image": {
    "@type": "blob",
    "content_type": "image/jpeg",
    "digest": "sha1-S8asPSgzA+F+fp8/2DdIy4K+0U8=",
    "length": 14989
  },
  "name": "priya Rajagopal",
  "type": "user",
  "university": "British Institute in Paris, University of London"
}'
```

iOSアプリをバックグラウンドで起動します。

Xcodeの[デバッグ Debug]メニューから[バックグラウンドフェッチのシミュレーション Simulate Background Fetach]を選択します。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176567/8d853e8e-ec0b-721e-edf0-13eaf80817bc.png)

コンソールログを観察します。アプリがSync Gatewayから最新の変更をプルしたことを示す一連のメッセージが表示されます


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176567/1e2b2c0a-f7b8-6f64-9c48-d11d2de09407.png)


フォアグラウンドでアプリを起動します。バックグラウンドで同期されたユーザープロファイルドキュメントが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176567/973f5dff-0201-4e54-31ac-59bc22d918f9.png)




## 関連情報


https://docs.couchbase.com/userprofile-couchbase-mobile/backgroundfetch/userprofile/background-fetch.html

https://qiita.com/chocoyama/items/61dc07bf3312f62fc565