Google Calendar APIをUnityで活用する

コンテンツ開発事業部、Unityエンジニアの平井です。

普段はゲームアプリのクライアント側をUnityで開発しております。
今回は直近のプロジェクトでGoogle Calendar APIを使用した時の話を書こうかと思います。 

この記事を書こうと思った経緯

新規開発のゲームアプリにてGoogleカレンダーとの連携を行い、
アプリ側で用意したカレンダーデータとユーザーのカレンダーデータをアプリ内で確認したい、という要件でした。
会社内でもGoogleカレンダーの連携の実装をした人がいなかったので、まず実装方法の調査から行いました。
が、Unityで使用している記事が見つからなかったので、自分で書いてやろうと思った次第です。

ネイティブアプリで実装している記事は見つかったので、プラグインを活用して実装を行うことにしました。
Unityとプラグインの繋ぎ込み、外部ブラウザ側の設定でハマったところを中心に解説出来ればと思います。
今後Unity上でGoogleカレンダー連携を実装する人の参考になれれば幸いです。

前提

アプリ側で用意したカレンダーデータとユーザーのカレンダーデータの結合はサーバー側にお願いしました。
今回アプリ側で必要な処理としては、

1.OAuth認証でGoogleアカウントにサインインする
2.アカウントの認証情報をサーバーに送る

の2点になります。

Google Cloud Platformの設定

1. Google Cloud Platformにて新しいプロジェクトを作成する
Google Calendar APIを使用するにはGoogle Cloud Platformのプロジェクトが必要になります。
GoogleAPIsGoogleDeveloperConsole等色んな名前がありますが、基本的には同じサービスを指しているようです。

2. ライブラリからGoogle Calendar APIを有効にする
今回はGoogle Calendar APIのみですが、他のAPIも使用する場合同じように有効にする必要があります。

3. 認証情報からOAuth2.0クライアントIDを作成する
Android、iOS、ウェブアプリケーション用のものをそれぞれ作成します。 開発(デバッグ)と本番(リリース)でPackageNameやBundleIDが変わってくると思うので、それぞれクライアントIDを作成する必要があります。
※今回のプロジェクトではそもそも開発と本番でプロジェクト自体を分けたので、それぞれ一つしか用意していません。 f:id:teco_hirai:20200129121949p:plain

Androidの注意点として、keystoreのフィンガープリントが必要になります。
また、デバッグ用のkeystoreはバイナリをビルドするPCのkeystoreを参照するようにして下さい。
バイナリで使われているkeystoreと指定したkeystoreが違うとOAuth認証時にエラーが出ます。 f:id:teco_hirai:20200129122021p:plain

4. OAuth同意画面の設定
UserTypeは外部で作成します。
アプリケーション名、アプリのロゴ、サポートメール、[アプリケーション ホームページ] リンク、[アプリケーション プライバシー ポリシー] リンクの設定をします
(開発用であればなくても問題ないですが、本番用では記述されていないと承認を受けることが出来ません) f:id:teco_hirai:20200129122035p:plain

スコープ
GoogleAPIのスコープにカレンダー用スコープを追加します。
下記のページにカレンダーで使用するスコープの一覧があります。
https://developers.google.com/calendar/auth?hl=ja

今回は読み取りと書き込み両方を行うつもりでしたので、https://www.googleapis.com/auth/calendarを指定しました。
後述しますが、OAuth認証の同意画面はGoogleからの承認を受ける必要がありその際に定義したスコープの正当性を確認しているようです。
承認が受けられない可能性がありますので、正しいスコープを選択した方が良いかと思います。
(読み取りしか行わないのならcalendar.readonlyにする等)

承認済みドメイン
リンクで指定しているURIはこちらに記述したドメインでないと設定できません。
また、Webクライアントのコールバック設定などでURIを指定している場合もこちらにドメインを記述しておかないと設定できません。

プラグインとの繋ぎ込み

今回プラグイン側にお願いしたのはOAuth認証の処理と認証完了後に発行されるAuthCodeをUnity側に返す処理です。
プラグイン側での注意点をかいつまんで説明していきます。

■導入準備
Androidはgradleに下記を追加しました

    implementation 'com.android.support:design:28.0.0'
    implementation 'com.android.support:support-v4:28.0.0'
    implementation 'com.google.android.gms:play-services-auth:16.0.0'
    implementation('com.google.api-client:google-api-client-android:1.22.0') {
        exclude group: 'org.apache.httpcomponents'
    }
    implementation('com.google.apis:google-api-services-calendar:v3-rev214-1.22.0') {
        exclude group: 'org.apache.httpcomponents'
    }

iOSは下記フレームワークを追加しています

GoogleSignIn.bundle
GoogleSignIn.framework
GoogleSignInDependencies.framework

1. サインインの処理の初期化の際にスコープの設定とクライアントIDの設定を行う

■Android kotlin

val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
    .requestScopes(Scope(SCOPES[0]))
    .requestIdToken(clientId)
    .requestEmail()
    .requestServerAuthCode(clientId, true)
    .build()

■iOS Objective-C

- (void)init:(NSString*_Nonnull)clientID server:(NSString*_Nonnull)serverClientID {
    
    [GIDSignIn sharedInstance].clientID = clientID;
    [GIDSignIn sharedInstance].serverClientID = serverClientID;
    [GIDSignIn sharedInstance].scopes = @[@"https://www.googleapis.com/auth/calendar"];
    [GIDSignIn sharedInstance].shouldFetchBasicProfile = NO;
}

■スコープ
OAuth同意画面の設定で選択したスコープと同じものを設定するようにしましょう。

■クライアントID
AndroidではウェブアプリケーションのクライアントIDを、
iOSでは.clientIDにiOSのクライアントID、.serverClientIDにウェブアプリケーションのクライアントIDを、それぞれ渡す必要があります。
注意点として、AndroidではAndroidのクライアントIDではなく、ウェブアプリケーションのクライアントIDでないとサインイン時にエラーが発生します。

2. サインイン完了後、AuthCodeを取得する
当初はアクセストークンとリフレッシュトークンをサーバーに渡そうと思っていましたが、
両OSともリフレッシュトークンを取得するAPIが用意されていませんでしたので、
serverAuthCodeを渡してサーバー側でトークンを発行する形にしました。
なお、serverAuthCode自体は数分で有効期限が切れるようなので、取得後すぐに送らないと発行できない可能性があります。

■Android kotlin

val authcode = GoogleSignInAccount?.getServerAuthCode()!!

■iOS Objective-C

NSDictionary *dict = @{@"serverAuthCode": (GIDGoogleUser *).serverAuthCode};

3. iOSで処理するためのコールバックを設定する
info.plistの「URL Types」に以下2点を追加してください。

CFBundleURLSchemes [REVERSED_CLIENT_ID]
CFBundleURLSchemes [BUNDLE_ID]

※REVERSED_CLIENT_IDはGoogle Cloud Platform > 認証情報のiOSクライアント内「iOSのURLスキーム」で設定されているものです。

正しく設定出来ていればinfo.plistが下の画像のようになります。 f:id:teco_hirai:20200129122105p:plain

実機確認

ここまで行えれば開発用では動作の確認が行えます。
Androidは前述したようにビルドするPCには気を付けましょう。
クライアントIDを作成する時に指定したKeyStoreを持つPCでビルドしないとAPI実行時にエラー(エラーコード:12500)が返ってきます。

本番用の設定

これ以降の設定はStoreで公開する時に必要な設定になります。
Storeでの公開を行わない場合は無視して下さい。

1. 本番用のOAuth2.0クライアントIDを作成する
BundleIDやPackageNameが開発と同じ場合は不要です。
Androidは本番用のkeystoreのフィンガープリントを用意して下さい。
クライアントIDが作成出来たらpluginに渡すクライアントIDを本番用に変更します。

2.OAuth同意画面の承認
OAuth同意画面の承認を受けていない場合、iOSでのサインイン時に下記画面が出ます。 この画面を表示させないために承認は受けましょう。
f:id:teco_hirai:20200130172541p:plain

開発で各項目を入力していなかった場合、承認が受けられないので設定して下さい。
問題なければ画面下の方の「確認のため送信」ボタンを押すと送信画面が出ます。 スコープの理由と連絡先メールアドレスを記載してさらに送信を押すと申請が行えます。
(例で書いてあるのと同じような感じで書けば問題ないかと思います)

この承認は完了するまで1週間程掛かる可能性があります。
また、何度かメールで質問が来たり、実際に連携している様子を動画で送る必要もありますので、事前に受けておきましょう。
※一度送信後、内容を変更した場合は、再度送信する必要があります。
※送信後は保存ボタンが押せなくなるため、一時保存を行う場合も再度送信しないと保存できません。

3. FirebaseGoogle Play Consoleのリンク
リリースバイナリの場合、リンクを行っていないと「ApiException: 10」が出ます。
Google Cloud PlatformプロジェクトとリンクしているFirebaseプロジェクトが作られていない場合は作成しましょう。
設定 > 全般 > マイアプリにアプリが設定されていない場合は追加して下さい。
設定 > 統合 > GooglePlayにてリンクするGoogle Play Consoleとリンクを行います。
Google Play Consoleのオーナーアカウントでないとリンクは設定できません

f:id:teco_hirai:20200129122119p:plain 画像のようにステータスがリンク済みになっていればリンクされています。

まとめ

自分がはまったところを中心に解説させて頂きました。
GoogleAPIは公式ドキュメントはありますが、実装の記事が少ないので手探りの状態で開発してました。
必要な箇所をまとめられたかと思うので、参考にして頂ければ幸いです。

tecotec.co.jp