去年(2016年)にこんな話題が一部で盛り上がった。
ネイティブ アプリの OAuth インタラクションを最新にしてユーザビリティとセキュリティを向上する - Google Developers Japan
要は今までのやり方である「WebViewでGoogleの認証を行う」というのが2017年4月20日以降は使えなくなるというものだ。
これは開発者にとって地味に困ったことだ。
以前に実装したものが動かなくなるし、新しい実装を行うにも工数がかかる。
以前作成したサンプルアプリを動かしてみるとこのようになった。

確かに以前のやり方は使えなくなる旨の注意が表示されている。
では、どういうやり方に変えればいいか。
Google Sign-Inを使えばいいとのことだ。
今回はGoogle Sign-Inを使ってカレンダーの情報を取得するやり方に迫る。
使用しているXcodeのバージョンは8.0だ。
ネイティブ アプリの OAuth インタラクションを最新にしてユーザビリティとセキュリティを向上する - Google Developers Japan
要は今までのやり方である「WebViewでGoogleの認証を行う」というのが2017年4月20日以降は使えなくなるというものだ。
これは開発者にとって地味に困ったことだ。
以前に実装したものが動かなくなるし、新しい実装を行うにも工数がかかる。
以前作成したサンプルアプリを動かしてみるとこのようになった。

確かに以前のやり方は使えなくなる旨の注意が表示されている。
では、どういうやり方に変えればいいか。
Google Sign-Inを使えばいいとのことだ。
今回はGoogle Sign-Inを使ってカレンダーの情報を取得するやり方に迫る。
使用しているXcodeのバージョンは8.0だ。
Google Sign-Inを使ってカレンダーにアクセスするにはどうすれば良いか
Google Sign-Inはあくまでもサインインを制御するためのライブラリであって、カレンダーやGMAILにアクセスする方法はここには含まれていない。
前述の開発者ブログにはAppAuthというライブラリを使えばいいというようなことが書かれているので少々調べてみたがよくわからなかった。
iOS Quickstartのようにステップバイステップで紹介されていないのでわかりづらいのだ。
余談だが、上記のiOS Quickstartは情報が古いままで更新されていないため、そのまま作ると件の注意が表示される。
そもそもSwift3で書かれていないため、そのままコピペするとエラーがたくさん出る。
注意したい。
ではどのようにすればよいか試行錯誤したところ
Google Sign-Inでサインインを行って、カレンダーへのアクセスはREST APIを使う
という方法に落ち着いた。
今後もっと良いやり方を見つけたらそちらに移行するが、当面はこれで我慢しよう。
とにかく、海外サイトも含めて新しいやり方に関する情報が少なすぎて現段階では如何ともしがたいのだ。
ただし、メリットもある。
サインインしてしまえば、それから後の処理(カレンダーにアクセスしたりGMAILにアクセスしたり)はHTTPS通信で行えるので、専用の複雑なクラスを使うよりソースコード的にはわかりやすくなることだろう。
Google Sign-Inでサインインを行う
ではまずGoogle Sign-Inでサインインを行うやり方を見ていこう。
参考にするのはここだ。
Try Sign-In for iOS
CocoaPodsを使うことが推奨されているが、ここではあえて使わないやり方でいこうと思う。
前準備
まず新しいSingle View Applicationを作る。
次にGet the Google Sign-In SDK for iOSから最新のSign-In SDKをダウンロードする。
この記事を書いてる時点では4.0.1だ。
プロジェクトへSDKを追加する
ダウンロードしたzipを解凍し、以下の5つのファイルをプロジェクトにドラッグする。

公式ページにはこの後GoogleSignIn.bundleをBuild PhasesのCopy Bundle Resourcesに追加するように書いてあるが、どうやら自動で追加されているようなので、この作業は必要無さそうだ。
必要なフレームワークの追加
Build PhasesのLink Binary With Librariesの+ボタンを押して以下の二つを追加する。

次にBuild Settingsを開き、Other Linker Flagsに
$(OTHER_LDFLAGS) -ObjC
を追加する。

認証情報を作成する
Google Developer Consoleで新しい認証情報を作成する。

作成は"OAuthクライアントID"→"iOS"を選択する。
名前は適当に入力して、バンドルIDは今回のアプリのものを入れる。
※API ManagerでGoogle Calendar APIを有効にすることも忘れずに行っておく。
URLスキームをプロジェクトに追加する
公式では次にconfigurationファイルを作成するようになっているが、これはリバースクライアントIDを求めるための作業なので、今回はあえて除外してみる。
リバースクライアントIDは先ほど作成したクライアントIDを逆にしたものだ。
例えばクライアントIDが
123456789.apps.googleusercontent.com
だったら
リバースクライアントIDは
com.googleusercontent.apps.123456789
となる。
これを踏まえた上でProject → Targets → Info → URL Typesを開き、+ボタンを押す。
URL SchemesにリバースクライアントIDを入力する。
他の項目は何も入力しなくて良い。

これで準備は完了だ。
次は実装を行ってみる。
サインインの実装を行う
サインイン用のヘッダファイル読み込み
まずBridging-Header.hを作成し、Build SettingsのObjective-C Bridging Headerを設定する。

次にBridging-Header.hの中に以下のようにサインイン用のヘッダを追加する。
Google/SignIn.hではなくGoogleSignIn/GoogleSignIn.hを使うのはCocoaPodsを使わずに手動でSDKを設定したためだ。
以降も同じような箇所があるが、言及は省略する。
AppDelegate.swiftの実装を行う
まず以下のようにGIDSignInDelegateプロトコルを追加する。
次にapplication:didFinishLaunchingWithOptions:を以下のようにする。
クライアントIDは各自のものに置き換えること。
次にapplication:openURL:options:を以下のように実装する。
iOS8以下の場合はapplication:openURL:sourceApplication:annotation:も設定する必要があるようだが、今回は省略する。
次にサインインが接続された時に呼ばれるメソッドと接続が切れた時に呼ばれるメソッドを以下のように実装する。
ここでは自分の名前とメールアドレスを出力するようにしている。
サインインを呼び出すViewController側の実装を行う
Storyboardで以下のようにサインインとサインアウトのボタンを配置し、それぞれにアクションを設定する。

次にViewControllerにGIDSignInUIDelegateプロトコルを追加する。
これを忘れると実行時にアプリが落ちてしまうので注意しよう。
それではここでサインインを実行してみる。

認証画面が表示された。
まずは第一段階は成功だ!
では許可ボタンを押してみよう。

個人情報なのでモザイクをかけてはいるが、自分の名前とメールアドレスが出力されている。
サインインが行われたということだ。
Googleカレンダーのイベントを取得する
いよいよ最後の作業だ。
次のように設定してあるカレンダーのイベントを取得してみよう。

REST APIを使用する
REST APIは通常のHTTPSで利用するので、NSURLSessionで大丈夫そうだ。
AppDelegate.swiftの中のサインイン完了後に以下のようにメソッドを呼び出すようにしよう。
getEvents()はイベントを取得する処理を行うメソッドで、この後に実装する。
カレンダーのアクセスにはアクセストークンが必要なので、引数として渡しておく。
それではイベントを取得するメソッドを実装しよう。
基本的には普通にHTTPS通信を行っているだけだ。
ポイントを見ていこう。
1.イベントリストを取得するURLを設定する。
primaryカレンダー以外のカレンダーにアクセスしたい場合は別途カレンダーIDを設定しよう。
ここではパラメータを渡して1/13〜1/14のイベントのみを取得するようにしている。
2.リクエストヘッダに認証情報を設定する。
ここでアクセストークンを設定する。
これが無いとアクセスできない。
Bearerを付けるのを忘れないこと。
3.通信を行う。
レスポンスはJSONで帰ってくるので、イベントの内容のみ出力するようにしている。
さあ、それじゃあ実行してみよう。

無事イベントを取得することが出来た!
Google Sign-Inはあくまでもサインインを制御するためのライブラリであって、カレンダーやGMAILにアクセスする方法はここには含まれていない。
前述の開発者ブログにはAppAuthというライブラリを使えばいいというようなことが書かれているので少々調べてみたがよくわからなかった。
iOS Quickstartのようにステップバイステップで紹介されていないのでわかりづらいのだ。
余談だが、上記のiOS Quickstartは情報が古いままで更新されていないため、そのまま作ると件の注意が表示される。
そもそもSwift3で書かれていないため、そのままコピペするとエラーがたくさん出る。
注意したい。
ではどのようにすればよいか試行錯誤したところ
Google Sign-Inでサインインを行って、カレンダーへのアクセスはREST APIを使う
という方法に落ち着いた。
今後もっと良いやり方を見つけたらそちらに移行するが、当面はこれで我慢しよう。
とにかく、海外サイトも含めて新しいやり方に関する情報が少なすぎて現段階では如何ともしがたいのだ。
ただし、メリットもある。
サインインしてしまえば、それから後の処理(カレンダーにアクセスしたりGMAILにアクセスしたり)はHTTPS通信で行えるので、専用の複雑なクラスを使うよりソースコード的にはわかりやすくなることだろう。
Google Sign-Inでサインインを行う
ではまずGoogle Sign-Inでサインインを行うやり方を見ていこう。
参考にするのはここだ。
Try Sign-In for iOS
CocoaPodsを使うことが推奨されているが、ここではあえて使わないやり方でいこうと思う。
前準備
まず新しいSingle View Applicationを作る。
次にGet the Google Sign-In SDK for iOSから最新のSign-In SDKをダウンロードする。
この記事を書いてる時点では4.0.1だ。
プロジェクトへSDKを追加する
ダウンロードしたzipを解凍し、以下の5つのファイルをプロジェクトにドラッグする。
- GoogleSignIn.bundle
- GoogleSignIn.framework
- GoogleAppUtilities.framework
- GoogleSignInDependencies.framework
- GoogleSymbolUtilities.framework

公式ページにはこの後GoogleSignIn.bundleをBuild PhasesのCopy Bundle Resourcesに追加するように書いてあるが、どうやら自動で追加されているようなので、この作業は必要無さそうだ。
必要なフレームワークの追加
Build PhasesのLink Binary With Librariesの+ボタンを押して以下の二つを追加する。
- SafariServices.framework
- SystemConfiguration.framework

次にBuild Settingsを開き、Other Linker Flagsに
$(OTHER_LDFLAGS) -ObjC
を追加する。

認証情報を作成する
Google Developer Consoleで新しい認証情報を作成する。

作成は"OAuthクライアントID"→"iOS"を選択する。
名前は適当に入力して、バンドルIDは今回のアプリのものを入れる。
※API ManagerでGoogle Calendar APIを有効にすることも忘れずに行っておく。
URLスキームをプロジェクトに追加する
公式では次にconfigurationファイルを作成するようになっているが、これはリバースクライアントIDを求めるための作業なので、今回はあえて除外してみる。
リバースクライアントIDは先ほど作成したクライアントIDを逆にしたものだ。
例えばクライアントIDが
123456789.apps.googleusercontent.com
だったら
リバースクライアントIDは
com.googleusercontent.apps.123456789
となる。
これを踏まえた上でProject → Targets → Info → URL Typesを開き、+ボタンを押す。
URL SchemesにリバースクライアントIDを入力する。
他の項目は何も入力しなくて良い。

これで準備は完了だ。
次は実装を行ってみる。
サインインの実装を行う
サインイン用のヘッダファイル読み込み
まずBridging-Header.hを作成し、Build SettingsのObjective-C Bridging Headerを設定する。

次にBridging-Header.hの中に以下のようにサインイン用のヘッダを追加する。
Google/SignIn.hではなくGoogleSignIn/GoogleSignIn.hを使うのはCocoaPodsを使わずに手動でSDKを設定したためだ。
以降も同じような箇所があるが、言及は省略する。
AppDelegate.swiftの実装を行う
まず以下のようにGIDSignInDelegateプロトコルを追加する。
次にapplication:didFinishLaunchingWithOptions:を以下のようにする。
クライアントIDは各自のものに置き換えること。
次にapplication:openURL:options:を以下のように実装する。
iOS8以下の場合はapplication:openURL:sourceApplication:annotation:も設定する必要があるようだが、今回は省略する。
次にサインインが接続された時に呼ばれるメソッドと接続が切れた時に呼ばれるメソッドを以下のように実装する。
ここでは自分の名前とメールアドレスを出力するようにしている。
サインインを呼び出すViewController側の実装を行う
Storyboardで以下のようにサインインとサインアウトのボタンを配置し、それぞれにアクションを設定する。

次にViewControllerにGIDSignInUIDelegateプロトコルを追加する。
これを忘れると実行時にアプリが落ちてしまうので注意しよう。
それではここでサインインを実行してみる。

認証画面が表示された。
まずは第一段階は成功だ!
では許可ボタンを押してみよう。

個人情報なのでモザイクをかけてはいるが、自分の名前とメールアドレスが出力されている。
サインインが行われたということだ。
Googleカレンダーのイベントを取得する
いよいよ最後の作業だ。
次のように設定してあるカレンダーのイベントを取得してみよう。

REST APIを使用する
REST APIは通常のHTTPSで利用するので、NSURLSessionで大丈夫そうだ。
AppDelegate.swiftの中のサインイン完了後に以下のようにメソッドを呼び出すようにしよう。
getEvents()はイベントを取得する処理を行うメソッドで、この後に実装する。
カレンダーのアクセスにはアクセストークンが必要なので、引数として渡しておく。
それではイベントを取得するメソッドを実装しよう。
基本的には普通にHTTPS通信を行っているだけだ。
ポイントを見ていこう。
1.イベントリストを取得するURLを設定する。
primaryカレンダー以外のカレンダーにアクセスしたい場合は別途カレンダーIDを設定しよう。
ここではパラメータを渡して1/13〜1/14のイベントのみを取得するようにしている。
2.リクエストヘッダに認証情報を設定する。
ここでアクセストークンを設定する。
これが無いとアクセスできない。
Bearerを付けるのを忘れないこと。
3.通信を行う。
レスポンスはJSONで帰ってくるので、イベントの内容のみ出力するようにしている。
さあ、それじゃあ実行してみよう。

無事イベントを取得することが出来た!
コメント
コメント一覧 (2)
参考にさせて頂きありがとうございます
小さなミスなんですがリスト6-1のファイル名が重複してるようです
6-1 AppDelegate.swift
6-2 ViewController.swift
ですね。
ご指摘ありがとうございます。
修正しました。