branch10480’s blog

Topics that I've learned.

iOS Firebase Authentication Twitter ログイン実装でハマったこと

今日は仕事で開発しているマッチングアプリに Twitter ログインを付けるということで Firebase の Authentication で実装を進めていました。

が、1箇所?ハマったところがあったので備忘録も兼ねて記録を残しておこうかと思います。

f:id:branch10480:20200705141819p:plain

結論

OAuthProvider はプロパティに入れ込み、参照カウントを1増やしておくことが重要!

class HogeViewController: UIViewController {

    /// ARCの参照数を上げるために必要、これを設けないと処理が実行されない
    private var oAuthProvider: OAuthProvider?


    /* ...中略... */


    /// Twitterでログイン タップ時
    @IBAction func didTapTwitterLoginButton(_ sender: Any) {
        oAuthProvider = OAuthProvider(providerID: "twitter.com")
        oAuthProvider?.getCredentialWith(nil) { [weak self] credential, error in

            // 成功、失敗時の処理を書いていく
        }
    }
}

何が起こった?

Firebase のドキュメント読みつつ進めていたんですが、Twitter ログインボタン押下時に以下の処理を書いたんですけど <<1>> の部分が動いていない。。

/// Twitterでログイン タップ時
@IBAction func didTapTwitterLoginButton(_ sender: Any) {
    let oAuthProvider = OAuthProvider(providerID: "twitter.com")
    oAuthProvider.getCredentialWith(nil) { [weak self] credential, error in
        // <<1>>
    }
}

ドキュメント見比べつつ間違いが無いか何度も確認しましたがおかしいところはなさそう。。

うーん😿

記事を漁る漁る。。

解決!!

そこで見つけた解決策は OAuthProvider(providerID: "twitter.com") をプロパティに保存し、オブジェクトの参照カウントを1上げておくこと。

そうすることで、オブジェクトは開放されずに処理が実行されると。

Twitter ボタン押下時時のハンドラ内に OAuthProvider(providerID: "twitter.com") を生成してローカル変数に入れただけでは、ボタン押下時のメソッド実行完了時にメモリ解放されてしまうということですね。

まとめ

インスタンス生成、その処理を実装して動かないときはメモリ解放を疑う。
最近こういう場面に遭わなかったので、その可能性を思い付けませんでした。。

今後の私の戒めと、同じ場面で悩んでいる方がいるかも知れないので記事を書いてみました!

...

おまけ

Firebase の SDK は更新されるので、ドキュメント通り実装しても動かないことはたまにあるんですけど、それにしても現在記載されているコードではそもそも文法上コンパイルに失敗すると思うんですが。。🤔 (ここが冒頭で1箇所?としていた理由)

    provider.getCredentialWith(nil) { credential, error in
      if error != nil {
        // Handle error.
      }
      if credential != nil {
        Auth().signIn(with: credential) { authResult, error in
          if error != nil {
            // Handle error.
          }
          // User is signed in.
          // IdP data available in authResult.additionalUserInfo.profile.
          // Twitter OAuth access token can also be retrieved by:
          // authResult.credential.accessToken
          // Twitter OAuth ID token can be retrieved by calling:
          // authResult.credential.idToken
          // Twitter OAuth secret can be retrieved by calling:
          // authResult.credential.secret
        }
      }
    }

credential が nil でないことを確認しているんですけど、使っている部分は元々のオプショナル credential そのまま使っているんですよね。

今の Swift の文法だとこうですかね。

guard let credential = credential else { return }
Auth.auth().signIn(with: credential) { authResult, error in
    // 略
}

昔のSDKは signIn(with: A) ... のA部分はオプショナルだったのかな??