Google Sign-InのDelegateをRxSwift対応させてどこからでも簡単に呼び出す

開発中のプロジェクトでGoogle APIを使うことになり、アカウント管理を含めGoogleサインインを導入しました。
ただ、その際に公式のframeworkのまま使おうとした場合に、今回の要件上イケてないと感じた部分がいくつかあったため、
RxSwiftを用いた拡張フレームワークを作成しました。

今回の実装のサンプルコードはオープンソースで公開しました。

- https://github.com/kazumanagano/GIDSignIn-Rx


##前提条件
- Swift 4.2
- Xcode 10.0
- 公式ドキュメント通りに動かすことはできてる前提( Cloud Console上の設定、ライブラリ導入など)

##### 導入方法で参考にしたもの
 - [Qiita: Google Sign-In for iOS](https://qiita.com/shin1ogawa/items/0700c443262ba3d444a7) @shin1ogawa
 - [Google公式ドキュメント](https://developers.google.com/identity/sign-in/ios/)

##イケてないポイント
今回取り組んだイケてないポイントは次の２点です。

　**　1. DelegateじゃなくRxで非同期に処理を扱いたい**
　**　2. signInSilently() できない！？(扱いづらい）**

##1. DelegateじゃなくRxで非同期に処理を扱いたい
**GoogleSignIn Framework**はDelegateを利用し、ログインアクションの結果を受け取る仕様になっています。
しかし今回のアプリではRxSwiftを使用しMVVMで実装を行なっていたため、できればログインアクションをリアクティブに
行い、関連処理をVM内のメソッドチェーンでコンパクトに処理したいと思いました。

RxSwiftにはDelegateメソッドを扱うための**DelegateProxy**というものがあったので今回 [@tako3a の記事](http://tiny-wing.hatenablog.com/entry/2016/04/18/085621)
と[RxSwiftのGithub上に上がっていたIssue](https://github.com/ReactiveX/RxSwift/issues/1669)を参考にGIDSignInクラスのRxエクステンションを実装しました。

####GIDSignInDelegateのDelegateProxyを実装
```swift:GIDSignIn+Rx.swift
 import GoogleSignIn
 import RxSwift
 import RxCocoa
 
 class RxGIDSignInDelegateProxy: DelegateProxy<GIDSignIn, GIDSignInDelegate>,GIDSignInDelegate  {
     public weak private(set) var gidSignIn: GIDSignIn?
     var signInSubject = PublishSubject<GIDGoogleUser>()
     
     init(gidSignIn: ParentObject) {
         self.gidSignIn = gidSignIn
         super.init(parentObject: gidSignIn, delegateProxy: RxGIDSignInDelegateProxy.self)
     }
     
     func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
         if let u = user {
             signInSubject.on(.next(u))
         } else if let e = error {
             signInSubject.on(.error(e))
         }
         _forwardToDelegate?.sign(signIn, didSignInFor:user, withError: error)
     } 
     
     deinit {
         signInSubject.on(.completed)
     }
 }
 
 extension RxGIDSignInDelegateProxy :DelegateProxyType {
     static func registerKnownImplementations() {
         register { RxGIDSignInDelegateProxy(gidSignIn: $0) }
     }
     
     static func currentDelegate(for object: GIDSignIn) -> GIDSignInDelegate? {
         return object.delegate
     }
     
     static func setCurrentDelegate(_ delegate: GIDSignInDelegate?, to object: GIDSignIn) {
         object.delegate = delegate
     }
 }
 
 extension Reactive where Base: GIDSignIn {
     public var delegate: DelegateProxy<GIDSignIn, GIDSignInDelegate> {
         return self.gidSignInDelegate
     }
     
     var signIn: Observable<GIDGoogleUser> {
         let proxy = self.gidSignInDelegate
         proxy.signInSubject = PublishSubject<GIDGoogleUser>()
         return proxy.signInSubject
             .asObservable()
             .do(onSubscribed: {
                 proxy.gidSignIn?.signIn()
             })
             .take(1)
             .asObservable()
     }
 }
```
細かい説明は省きますが、DelegateProxyとして`RxGIDSignInDelegateProxy`を定義し、
`signInSubject:PublishSubject<GIDGoogleUser>`を変数として持たせます。
Reactive Extention上で
`signIn: Observable<GIDGoogleUser>`
としてそれをラップした上で**do**メソッドとして**GIDSignInクラスのsignIn()**を呼んでいます。
利用する側としては

```swift:LoginVIewModel.swift
let login = GIDSignIn.sharedInstance().rx.login
                .flatMap({ (user:GIDUser) in
                  //GIDUserを使ってなんか処理
                })
                .share(replay: 1)
```
のようにDelegateの実装なしに晴れてメソッドチェーンを繋げていくことができます！

##2. signInSilently() できない！？(扱いづらい）
無事にスマートなログイン処理が実装できたのですが、この後も問題が発覚しました。
ログインを終えたユーザにはGIDSignInクラスには**signInSilently**というメソッドが用意されています。
>// Attempts to sign in a previously authenticated user without interaction.  The delegate will be
// called at the end of this process indicating success or failure.
- (void)signInSilently;

ところが下記のコードではユーザ情報が取得できませんでした。

```swift:AppDelegate.swift
 import UIKit
 import GoogleSignIn
 
 @UIApplicationMain
 class AppDelegate: UIResponder, UIApplicationDelegate  {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions 
     							launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
         GIDSignIn.sharedInstance().clientID = "your client ID"
         GIDSignIn.sharedInstance().scopes = ["your scopes"]
         
         GIDSignIn.sharedInstance().signInSilently()
         
         //ログインを１度した後再度起動するとtrueが返ってくる
         if GIDSignIn.sharedInstance().hasAuthInKeychain() {
             print(GIDSignIn.sharedInstance()?.currentUser)
             //なぜか常にnil。。
         }
         
         return true
     }
```
**currentUser**を呼ぶタイミングを遅延させたりいろいろと試してみましたが常にnilが返ってきてしまいます。
この現象は下記のStackOverFlowでも報告されていました。
[GIDSignIn.sharedInstance().currentUser nil when relaunching app](https://stackoverflow.com/questions/39830457/gidsignin-sharedinstance-currentuser-nil-when-relaunching-app)

#### 解決策
類似のStackOverFlowを探っていくと以下の情報を見つけました
[Swift+GIDSignIn - hasAuthInKeyChain returns true but currentUser is still nil after calling signInSilently](https://stackoverflow.com/questions/42960181/swift-gidsignin-hasauthinkeychain-returns-true-but-currentuser-is-still-nil)
>I had a similar issue. First I thought it was a timing issue. By invoking signIn.signInSilently() after fully loading the root viewController it did work. I am now 99% sure I solved it by executing signIn.signInSilently() on the main thread through

どうやら　**メインスレッドで動作させた方が良さそう**だと言っています。
また、 仕様で書かれている通りDelegateメソッドで結果を受け取った方が確実だと考えました。
ということで、**signin()**メソッドと同様に、ViewController上から**Rxエクステンション**として実装しました。

####GIDSignInDelegateのDelegateProxyを実装
```swift:GIDSignIn+Rx.swift
 
 extension Reactive where Base: GIDSignIn {
     public var delegate: DelegateProxy<GIDSignIn, GIDSignInDelegate> {
         return self.gidSignInDelegate
     }
     
     var signIn: Observable<GIDGoogleUser> {
         let proxy = self.gidSignInDelegate
         proxy.signInSubject = PublishSubject<GIDGoogleUser>()
         return proxy.signInSubject
             .asObservable()
             .do(onSubscribed: {
                 proxy.gidSignIn?.signIn()
             })
             .take(1)
             .asObservable()
     }
     
     //下記を追加実装
     var signInSilent: Observable<GIDGoogleUser> {
        let proxy = self.gidSignInDelegate
        proxy.signInSubject = PublishSubject<GIDGoogleUser>()
        return proxy.signInSubject
            .asObservable()
            .do(onSubscribed: {
                proxy.gidSignIn?.signInSilently()
            })
            .take(1)
            .asObservable()
    }
   
 }
```

これでユーザ情報が必要な場面で**signIn()**と同様に呼び出すことでGIDUserを取得することができるようになりました。



今回の実装のサンプルコード
https://github.com/kazumanagano/GIDSignIn-Rx