【iOS】 Cloud Functions for Firebaseを使ってアプリにサーバ日時取得処理を追加する

# はじめに
ライセンスの期限チェックやゲームの時間チート対策など、端末の日時ではなくサーバの日時が欲しいケースがあります。
[Google Firebase](https://firebase.google.com)の1機能であるCloud Functions for Firebaseを使うことで、容易にサーバ日時取得処理をアプリに追加できます。

# Cloud Functions for Firebaseとは
アプリなどの環境から、Googleクラウド上にデプロイしているバックエンドコードを実行することができる機能です。
毎月200万回、処理時間100万秒、トラフィック容量5GBまでであれば無料で利用できます。[2019/7/10現在](https://cloud.google.com/functions/pricing-summary/?hl=ja)

# 前提条件
この記事を利用するにあたり、次の状態を前提とします。
- 処理を追加するXcodeプロジェクトが存在する
- Google Firebase上にプロジェクトを作成し、Xcodeプロジェクトに導入している
- XcodeプロジェクトにCocoaPodsからFirebaseを導入している
- Xcodeプロジェクトで、Firebaseの初期化処理を実装している

CocoaPodsを使っていない場合など環境が異なる場合は、記事の該当部分について適宜対応してください。

# 環境の構築
上の項目から順番にインストールしていきます。
すでにインストール済みの場合、スキップして次の項目に移ってください。
## Node.jsとnpmのインストール
[Node.js公式サイト](https://nodejs.org/ja/)にアクセスし、インストーラーをダウンロードしインストールします。
インストール後ターミナルで次のコマンドを入力し、バージョン情報を得られたらインストール完了です。

```
$ node --version
$ npm --version
```

## Firebase CLIのインストール
ターミナルで次のコマンドを入力し、Firebase CLIをインストールします。

```
$ npm install -g firebase-tools
```

# バックエンド側の実装
## プロジェクトの作成
管理しやすい場所(Xcodeプロジェクトフォルダの直下など)に適当なフォルダを作成します。(以降バックエンドフォルダ)

```
$ mkdir FirebaseFunctions
```

ターミナルで作成したフォルダに移動し、次のコマンドを入力します。

```
$ firebase init
```

実行するといくつか選択項目が表示されるので、次の値を選択します。
- 利用する機能：Functions: Configure and deploy Cloud Functions
- デフォルトのFirebaseプロジェクト：作成したFirebaseプロジェクトを選択します
- 言語：JavaScript
- ESLintを利用するか：好みで選択します。単純な機能を実装するため、Nで問題ありません
- 今すぐ依存関係をインストールするか：Y
以上の手順でバックエンドフォルダ直下にファイル一式が作成されます。

## コードの記述
サーバ日時を取得するためのコードを記述します。
バックエンドフォルダ/functions/index.js に次のコードを追加します。

```バックエンドフォルダ/functions/index.js
exports.ServerTime = functions.https.onRequest((request, response) => {
    var dt = new Date();
    var delta = dt.getTime();
    response.send({
        data: String(delta)
    });
});
```

## コードのデプロイ
ターミナルで次のコマンドを入力し、ログインします。

```
$ firebase login
```

ログインしている状態で次のコマンドを入力し、デプロイを実行します。

```
$ firebase deploy
```

Deploy complete!と表示されたらデプロイ完了です。
ターミナルに出力されたProject ConsoleのURLを開き、Functionsからデプロイした関数を確認できます。

# Xcodeプロジェクト側の実装
## SDKのインストール
CocoaPodsからFirebaseFunctionsをインストールします。
podfileに次の行を追記します。

```
pod 'Firebase/Functions'
```

追記後ターミナルで次のコマンドを入力し、インストールを実行します。

```
$ pod install
```

## サーバ日時取得処理の実装
この記事ではサンプルとして、アプリ起動時にサーバ日時を取得しログ出力するよう実装します。
ViewController.swiftに次のコードを記述します。

```ViewController.swift
import FirebaseFunctions

class ViewController: UIViewController {
	override func viewDidAppear(_ animated: Bool) {
		super.viewDidAppear(animated)
		
		let functions = Functions.functions()
		functions.httpsCallable("ServerTime").call { result, error in
			if let error = error {
				debugPrint(error.localizedDescription)
			} else {
				if let data = result?.data as? String {
					let ms = UInt64(data) ?? 0
					let timeInterval = TimeInterval(ms) * 0.001
					let date = Date(timeIntervalSince1970: timeInterval)
					debugPrint("サーバ日時: \(date.description(with: Locale.current))")
				} else {
					debugPrint("受信データなし")
				}
			}
		}
	}
}
```

実行し、次のようなログが出力されれば成功です。

```
"サーバ日時: Wednesday, July 10, 2019 21:02:43 Japan Standard Time"
```

### メモ
サーバ日時はミリ秒で得られる為、Date型に変換する際は0.001を掛けて秒に変換する必要があります。

久しぶりにコードのメンテナンスを行おうとしたところ、`firebase init`で失敗する現象に遭遇しました。
コンソールから再ログインを行う事で解決しました。

```
$ firebase logout
$ firebase login
```