Swift3.0対応!Google Apps APIを使うためにGoogleサインインを組み込む方法

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

GoogleAppsとは、皆様ご存知のGmailやGoogleカレンダー、Googleドライブなどのクラウドグループウェアツールです。
GoogleAppsにはそれらの機能を使うためのGoogleAppsAPIが公開されています。
GoogleAppsAPIを使うとGmailやGoogleカレンダーなどのデータを取得することが出来ます。
これらのAPIをアプリから使うにはGoogleにサインインをする必要があります *1

今回はそんなGoogleAppsAPIを使うためにiOSアプリにGoogleサインインを組み込む方法になります。

環境

今回実施している環境は下記の通りです。

XCode 8.0
Swift 3.0
CocoaPods 1.0.0
iOS SDK 9.0 以上

準備

1. プロジェクトの作成

まずはXCodeでプロジェクトを新規に作成します。
今回はGoogleSignInSampleというプロジェクトを作成しました。
そしてBundle Identifierの値は設定で使うのでメモしておきましょう。

プロジェクト作成

そして、CocoaPodsで'Google/SignIn'をインストールします。

use_frameworks!
target 'GoogleSignInSample'
pod 'Google/SignIn'

プロジェクトを作成して'Google/SignIn'をインストールしたらGoogle側の設定になります。

2. Google側の設定

作成したプロジェクトでGoogleログインが使えるように設定します。
ブラウザで下記URLにアクセスします。

https://developers.google.com/identity/sign-in/ios/start

表示された画面に従って設定をします。

Swiftを選択します。

Try_Sign-In_for_iOS_ _ _Google_Sign-In_for_iOS_ _ _Google_Developers

下記はサンプルプロジェクトのダウンロードが出来るコマンドなので必要に応じて取得して下さい。(無くても大丈夫です)

Try_Sign-In_for_iOS_ _ _Google_Sign-In_for_iOS_ _ _Google_Developers 2

> pod try Google

GET A CONFIGURATION FILE ボタンをクリックする

Try_Sign-In_for_iOS_ _ _Google_Sign-In_for_iOS_ _ _Google_Developers_3

アプリケーションプロジェクトの登録

GET A CONFIGURATION FILE ボタンをクリックすると「Create or choose an app」ページが表示されます。
① App name には今回のアプリ名を、
② iOS Bundle ID には、プロジェクトの作成時にメモしたBundle Identifierの値を入力します。
入力したら下の Choose and configure services → ボタンをクリックしましょう。

Add_Google_Services_ _ _Google_Developers

Googleサインインの有効化

アプリケーションの登録が成功したら、今度はサインインを有効化します。
Google Sign-In を選択し、ENABLE GOOGLE SIGN-IN ボタンをクリックします。

Add_Google_Services_ _ _Google_Developers_2

有効化されると下記表示になります。
設定ファイル(GoogleService-Info.plist)をダウンロードする画面に遷移するために、
Generate configuration files → ボタンをクリックします。

Add_Google_Services_ _ _Google_Developers_3 2

GoogleService-Info.plistのダウンロード

下記画面が表示されたら、Download GoogleService-Info.plist ボタンをクリックして、GoogleService-Info.plistファイルをダウンロードしてください。

Add_Google_Services_ _ _Google_Developers_4 2

ダウンロードが完了したら、画面下部の Continue with Try Sign-In → ボタンをクリックします。

Add_Google_Services_ _ _Google_Developers_5

すると、一番最初の画面に戻ります。次からはXCodeでの操作に戻ります。

Try_Sign-In_for_iOS_ _ _Google_Sign-In_for_iOS_ _ _Google_Developers 4

3. GoogleService-Info.plist をプロジェクトに追加

先程ダウンロードしたGoogleService-Info.plistをプロジェクトに追加します。

GoogleService-Info_plistを追加する

表示すると下記のような設定値が登録されています。
その中から REVERSED_CLIENT_IDBUNDLE_ID の値をメモします。(BUNDLE_IDの値は最初にメモしたBundle Identifierの値と同じものです)

GoogleService-Info_plist

4. URL Typesの設定

プロジェクトファイル > Targets > Info > URL TypesREVERSED_CLIENT_IDBUNDLE_ID の値を登録します。
+を2回クリックして、それぞれの値を URL Schemes に設定します。

GoogleSignInSample_xcodeproj

5. ヘッダファイルの追加

{プロジェクト名}-Bridging-Header.h という名称のヘッダファイルを作成します。
※ 今回サンプルで作ったプロジェクト名はGoogleSignInSampleなので、その場合はGoogleSignInSample-Bridging-Header.hというファイル名になります。

ファイルの中身には

#import <Google/SignIn.h>

と記載します。

そして、 プロジェクトファイル > Targets > Build Settings > Swift Compiler - General 内の Objective-C Bridging Header に先程作成したファイル名、{プロジェクト名}-Bridging-Header.h を記載します。

GoogleSignInSample_xcodeproj_ヘッダを登録

ここまでで、ひと通りの準備が完了です。
次からコードの実装になります。

実装

下記ページを参考にコードを実装します。

Integrating Google Sign-In into your iOS app | Google Developers

AppDelegate.swift

まずはAppDelegate.swiftにコードを書いていきます。

まずは、 GIDSignInDelegateを受け取れるようにして、 func application(application:, didFinishLaunchingWithOptions:) -> Bool { ... } 内でサインインを初期化するコードを記述します。

// Initialize sign-in
var configureError: NSError?
GGLContext.sharedInstance().configureWithError(&configureError)
assert(configureError == nil, "Error configuring Google services: \(configureError)")
GIDSignIn.sharedInstance().delegate = self

GIDSignInDelegateの以下のメソッドを追加します。

func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) { ... }

sign()は認証処理の後呼び出されます。

また、GoogleサインインからのリダイレクトURLを受け取る為にopenURLに下記を記述します。

func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
    return GIDSignIn.sharedInstance().handle(url as URL!, sourceApplication: options[UIApplicationOpenURLOptionsKey.sourceApplication] as! String!, annotation: options[UIApplicationOpenURLOptionsKey.annotation])
}

サンプル

以下、サンプルコードです。

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Initialize sign-in
        var configureError: NSError?
        GGLContext.sharedInstance().configureWithError(&configureError)
        assert(configureError == nil, "Error configuring Google services: \(configureError)")
        GIDSignIn.sharedInstance().delegate = self

        return true
    }

    func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
        return GIDSignIn.sharedInstance().handle(url as URL!, sourceApplication: options[UIApplicationOpenURLOptionsKey.sourceApplication] as! String!, annotation: options[UIApplicationOpenURLOptionsKey.annotation])
    }
    
    〜 中略 〜
}

extension AppDelegate: GIDSignInDelegate {
    func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
        if let _ = error {
            // サインインが失敗
        } else {
            // サインインが成功
        }
    }
}

ログイン画面を呼び出す処理

ログイン画面を呼び出すViewController側の処理になります。

まずは、GIDSignInUIDelegateを受け取るようにして、

GIDSignIn.sharedInstance().uiDelegate = self

を記述します。

そして、GIDSignInUIDelegateのメソッドとして、

func sign(_ signIn:, present viewController:) { ... }

func sign(_ signIn:, dismiss viewController:) { ... }

を記述します。 presentの方はサインイン用のViewControllerが表示される時、 dismissの方は閉じられる時に呼び出されるので、

present側には

present(viewController, animated: true)

dismiss側には

dismiss(animated: true)

を記述します。

そして、ボタンを押したタイミングなどでサインインを実行する

GIDSignIn.sharedInstance().signIn()

を呼び出します。

サンプル

以下、サンプルコードです。

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        GIDSignIn.sharedInstance().uiDelegate = self
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    // サインインボタンを押したとき
    @IBAction func didTapSinginButton(_ sender: AnyObject) {
        GIDSignIn.sharedInstance().signIn()
    }
}

extension ViewController: GIDSignInUIDelegate {
    func sign(_ signIn: GIDSignIn!, present viewController: UIViewController!) {
        present(viewController, animated: true)
    }

    func sign(_ signIn: GIDSignIn!, dismiss viewController: UIViewController!) {
        dismiss(animated: true)
    }
}

以上がGoogleサインインを入れるにあたっての(おそらく)最低限の実装になります。

実行イメージ

サインインが実行されると下記のような画面が表示され、

実行イメージ

権限を聞かれます。

実行イメージ

サインイン後はAppDelegate.swift内のfunc sign(signIn: , didSignInFor user:, withError error:) が呼び出されます。
尚、画面左上のDone(完了)ボタンを押したり、権限を認めなかったりしても、エラー(失敗)で呼び出されます。

言語について

これらのログイン画面は端末の言語が反映されます。上記画像は英語になってますが、端末の言語設定を日本語にすれば、日本語表示になります。

日本語になる

権限(スコープ)について

Googleサインインには権限があります。
デフォルトだとEmailとprofileの取得になります。

スコープは必要に応じて変更できます。サインインを呼び出す前に、

GIDSignIn.sharedInstance().scopes = [`権限の配列`]

を記載します。

例えばGoogleスプレットシートの権限が必要な場合は

GIDSignIn.sharedInstance().scopes = ["https://www.googleapis.com/auth/spreadsheets"]

のようになります。

権限の値については下記URLをご覧ください。

スコープ一覧

https://developers.google.com/identity/protocols/googlescopes

デフォルトの権限について

また、Emailとprofileはデフォルトで入ってしまっているので、これらの権限が不要な時は、scopesを設定し、shouldFetchBasicProfilefalseにします。

// Googleスプレットシートの権限のみ
GIDSignIn.sharedInstance().scopes = ["https://www.googleapis.com/auth/spreadsheets"]
GIDSignIn.sharedInstance().shouldFetchBasicProfile = false

権限をかえてみた

脚注

  1. 厳密には、公開を制限しないでGoogleAppsScriptを書けば、一部はサインインしなくても使えますが...