Google Platform on iOS で簡単Google認証 2016年版


<img src="https://qiita-image-store.s3.amazonaws.com/0/25250/3f91fee3-b0c0-b0bf-9a26-5752c5c0227b.png" width="700">


#  はじめに

<img src="https://qiita-image-store.s3.amazonaws.com/0/25250/052aeb95-da2a-b8d3-0cd1-d177e5906165.png" width="480">

Google+ Sign-Inは非推奨になり、Google Sign-Inを使ってねとの事で、今回はその流れをメモとして残しておきたいと思います。

# 環境

- OS X El Captian 10.11.5
- Xcode7.3.1
- Swift2.2

# 手順

## Xcodeでプロジェクトを作成する

<img src="https://qiita-image-store.s3.amazonaws.com/0/25250/51901c49-a78a-afd3-fbb8-5009ec7b8730.png" width="600">

作成するアプリをお好きにどうぞ!!

## Google Developer Consoleでアカウントとアプリの登録

<img src="https://qiita-image-store.s3.amazonaws.com/0/25250/ca680423-614c-9a9f-3f74-243f9244e53a.png" width="600">

App nameとiOS Bundle IDを入力したら、```CONTINUE TO 「Choose and configure services」```をクリックして次へ進みます。

画面が切り替わり、使用するサービスを選択します。今回はSign-Inのみなので、```Google Sign-In```のみチェックを入れておきます。

<img src="https://qiita-image-store.s3.amazonaws.com/0/25250/c4006035-55c0-ec76-acad-2d0f1f819e88.png" width="600">

```CONTINUE TO 「Generate configuration files」```をクリックして、次へ進みます。

## plistファイルのダウンロード

そうすると、下のような画面に遷移するので```Download GoogleService-Info.plist```をクリックします。

<img src="https://qiita-image-store.s3.amazonaws.com/0/25250/e8b50278-a55c-64cd-4af8-e7b70297a0da.png" width="600">

上記のファイルがダウンロードされます。後ほど設定していきます。

## SDKのインストール

```Google Sign-In```のSDKは[cocoapods]()ではいります。

```
pod 'Google/SignIn'
```

```
$ pod install
Updating local specs repositories

CocoaPods 1.0.1 is available.
To update use: `sudo gem install cocoapods`
Until we reach version 1.0 the features of CocoaPods can and will change.
We strongly recommend that you use the latest version at all times.

For more information see http://blog.cocoapods.org
and the CHANGELOG for this version http://git.io/BaH8pQ.

Analyzing dependencies
Downloading dependencies
Installing FirebaseAnalytics (3.2.0)
Installing FirebaseInstanceID (1.0.6)
Installing Google (3.0.3)
Installing GoogleAppUtilities (1.1.1)
Installing GoogleAuthUtilities (2.0.1)
Installing GoogleInterchangeUtilities (1.2.1)
Installing GoogleNetworkingUtilities (1.2.1)
Installing GoogleSignIn (4.0.0)
Installing GoogleSymbolUtilities (1.1.1)
Installing GoogleUtilities (1.3.1)
Generating Pods project
Integrating client project

[!] Please close any current Xcode sessions and use `GGLLoginSample.xcworkspace` for this project from now on.
Sending stats
Pod installation complete! There is 1 dependency from the Podfile and 10 total
pods installed.
$
```

まぁ、もろもろインストールされました笑

## plistの追加

次にplistをプロジェクトに追加します。
ダウンロードしたplistファイルを作成したプロジェクトの直下に格納して、ナビゲーター・エリアにドラッグ＆ドロップします。

<img src="https://qiita-image-store.s3.amazonaws.com/0/25250/a622ad7a-a845-d46a-fa1f-87c248dd64a1.png" width="300">

## Bridging-Headerの作成

Objective-Cのファイルを作成しようとします

<img src="https://qiita-image-store.s3.amazonaws.com/0/25250/98e59664-bc6e-e22c-736b-f0ef9c78695c.png" width="600">

名前は適当にどうぞ!!

<img src="https://qiita-image-store.s3.amazonaws.com/0/25250/31a294f4-5a9a-817d-1b2f-3545e8564b76.png" width="600">

すると

<img src="https://qiita-image-store.s3.amazonaws.com/0/25250/77228d93-1693-84ac-19fb-361864d5f09f.png" width="600">

下記のようなダイアログが表示されますので、```Yes```を選択すると、Bridging-Headerが作成されます。

<img src="https://qiita-image-store.s3.amazonaws.com/0/25250/7e47a542-6173-9553-e7fb-5ba40aa849d5.png" width="400">

## URL Schemesの追加

plistファイルの中身は下記のようになっており（実際は右側にあたいが入っています）

<img src="https://qiita-image-store.s3.amazonaws.com/0/25250/ef5c38d6-1913-a321-c01b-54995bd840a6.png" width="300">

この、```REVERSED_CLIENT_ID```と、```BUNDLE_ID```のあたいを```URL Schemes```に追加します。

```Project```->```Target```->```Info```タブから設定します。

<img src="https://qiita-image-store.s3.amazonaws.com/0/25250/6fa43512-7417-b379-7281-d691f0df5382.png" width="700">

## ソースコードの改修

AppDelegateに```GIDSignInDelegate```protocolを継承させる。

```AppDelegate.swift
class AppDelegate: UIResponder, UIApplicationDelegate, GIDSignInDelegate
```

```application:didFinishLaunchingWithOptions:```メソッドに```GGLContext```のshared instanceとsign-in delegateを追記します。

```AppDelegate.swift
func application(application: UIApplication,
  didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> 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
}
```

>
注意：手動でSDKを組み込んだ場合は、```clientID```をコードで記述する必要があります。
```GoogleService-Info.plist```から探してください。
>
```
GIDSignIn.sharedInstance().clientID = kClientID
```

次に、AppDelegate.swiftに
```application:openURL:options:```メソッドを実装する。```GIDSignIn```のインスタンスのメソッドである```handleURL ```が適切にアプリケーションが認証プロセスの最後に受信したURLを処理します。

```AppDelegate.swift
func application(application: UIApplication,
  openURL url: NSURL, options: [String: AnyObject]) -> Bool {
    return GIDSignIn.sharedInstance().handleURL(url,
        sourceApplication: options[UIApplicationOpenURLOptionsSourceApplicationKey] as? String,
        annotation: options[UIApplicationOpenURLOptionsAnnotationKey])
}
```
iOS8以下の場合はこちら

```AppDelegate.swift
func application(application: UIApplication,
  openURL url: NSURL, sourceApplication: String?, annotation: AnyObject?) -> Bool {
    var options: [String: AnyObject] = [UIApplicationOpenURLOptionsSourceApplicationKey: sourceApplication,
                                        UIApplicationOpenURLOptionsAnnotationKey: annotation]
    return GIDSignIn.sharedInstance().handleURL(url,
        sourceApplication: sourceApplication,
        annotation: annotation)
```

AppDelegateでは、次のメソッドを定義することにより、サインインプロセスを処理するためにGIDSignInDelegateプロトコルを実装します。

```
func signIn(signIn: GIDSignIn!, didSignInForUser user: GIDGoogleUser!,
  withError error: NSError!) {
    if (error == nil) {
      // Perform any operations on signed in user here.
      let userId = user.userID                  // For client-side use only!
      let idToken = user.authentication.idToken // Safe to send to the server
      let fullName = user.profile.name
      let givenName = user.profile.givenName
      let familyName = user.profile.familyName
      let email = user.profile.email
      // ...
    } else {
      print("\(error.localizedDescription)")
    }
}
```

```
func signIn(signIn: GIDSignIn!, didDisconnectWithUser user:GIDGoogleUser!,
  withError error: NSError!) {
    // Perform any operations when the user disconnects from app here.
    // ...
}
```

>
注意：Sign-In SDKは、自動的にアクセストークンを取得しますが、アクセストークンは、あなたが```signIn```または```signInSilently```を呼び出したときにのみ更新されます。明示的にアクセストークンを更新するには、```refreshTokensWithHandler```を呼び出します。アクセストークンが必要な場合、、```getTokensWithHandler```を使用することでSDKが自動的にリフレッシュ処理をします。

## sign-inボタンの配置

ｰ ViewController.swiftを```GIDSignInUIDelegate```プロトコルを継承させます。

```ViewController.swift
class ViewController: UIViewController, GIDSignInUIDelegate {
```

- ViewController内で、```viewDidLoad```をoverrideして、viewDidLoadメソッド内で```GIDSignIn```オブジェクトのuiDelegateをセットします。また、```silently ```オプショナルで指定する事もできます。

```ViewController.swift
override func viewDidLoad() {
  super.viewDidLoad()

  GIDSignIn.sharedInstance().uiDelegate = self

  // Uncomment to automatically sign in the user.
  //GIDSignIn.sharedInstance().signInSilently()

  // TODO(developer) Configure the sign-in button look/feel
  // ...
}
```

>
注意：ユーザーが黙ってサインインすると、```signinSDK```は、自動的にアクセストークンを取得し、自動的に必要なときにそれらを更新します。方法：あなたがアクセストークンを必要とSDKが自動的にリフレッシュ処理したい場合は、```getAccessTokenWithHandler```を使用することができます。明示的にアクセストークンを更新するには、```refreshAccessTokenWithHandler```を呼び出します。

- ここまでは、```viewcontrlloer```はの```UIViewController```のサブクラスです。 該当のプロジェクトで、```GIDSignInUIDelegate```を実装するクラスが、```UIViewController```のサブクラスではない場合は
```GIDSignInUIDelegate```プロトコルのメソッドである```signInWillDispatch:error:```、```presentViewController:```、```signIn:dismissViewController:```を実装します。
こんな感じです。

```
// Implement these methods only if the GIDSignInUIDelegate is not a subclass of
// UIViewController.

// Stop the UIActivityIndicatorView animation that was started when the user
// pressed the Sign In button
func signInWillDispatch(signIn: GIDSignIn!, error: NSError!) {
  myActivityIndicator.stopAnimating()
}

// Present a view that prompts the user to sign in with Google
func signIn(signIn: GIDSignIn!,
    presentViewController viewController: UIViewController!) {
  self.presentViewController(viewController, animated: true, completion: nil)
}

// Dismiss the "Sign in with Google" view
func signIn(signIn: GIDSignIn!,
    dismissViewController viewController: UIViewController!) {
  self.dismissViewControllerAnimated(true, completion: nil)
}
```

```GIDSignInButton``` をStoryBoard、XIBファイルに追加するか、プログラムでインスタンス化します。StoryBoardまたはXIBファイルにボタンを追加するには、ビューを追加し、GIDSignInButtonにそのカスタムクラスを設定します。

>
StoryBoardにGIDSignInButtonビューを追加すると、サインインボタンは、インターフェイスビルダーでレンダリングされません。ログインボタンは、アプリを実行すると表示されます。

- ボタンをカスタマイズしたい場合は、次の手順を実行します。
ViewControllerでは、プロパティとしてログインボタンを宣言します。

```
@IBOutlet weak var signInButton: GIDSignInButton!
```

```signInButton``` プロパティにボタンを接続します。```GIDSignInButton``` オブジェクトのプロパティを設定することで、ボタンをカスタマイズする事ができます。

https://developers.google.com/identity/sign-in/ios/sign-in?configured=true

## ユーザのSign outについて

```GIDSignIn``` オブジェクトの```signOut``` メソッドを呼び出す事で、該当デバイスでサインアウトさせる事がきます。

```
@IBAction func didTapSignOut(sender: AnyObject) {
  GIDSignIn.sharedInstance().signOut()
}
```

<img src="https://qiita-image-store.s3.amazonaws.com/0/25250/f40d6365-414a-a134-1ada-a70f272df960.png" width="480">

## 遷移について

Google+ Sign-Inの時は、端末にGoogle+アプリがインストールされていれば、アプリが立ち上がっていたようですが、今回はNavigationControllerのPushでの遷移になり、SDKで内包しているWebViewが表示されます。（たぶん）

# 最後に

いかがだったでしょうか。記事が長くなってしまい、申し訳ありませんでした。最後までお読みいただきありがとうございました。
やる事は細々とありますが、とくにハマった点も無くイケました。
Firebaseとの違いがよくわかってないですが、便利なのはわかりました。ありがとうございました。


# 謝辞

- [Google Identity Platform
](https://developers.google.com/identity/)
- [Google Sign-In SDK for iOSを使ってみよう！](http://grandbig.github.io/blog/2015/09/13/google-sign-in/)
- [http://qiita.com/jiiikki/items/fd24b386d840b44e6028](http://qiita.com/jiiikki/items/fd24b386d840b44e6028)
- [googlesamples/google-services](https://github.com/googlesamples/google-services)