SwiftでTwitterのOAuth認証を使ってログインする方法のまとめ

SwiftでTwitterのOAuth認証を使ってログインする方法のまとめ
Pocket

ネイティブアプリの開発において、ユーザーの認証機能はとても重要です。今回はiOSアプリの開発においてログインをするときにTwitterのOAuth認証を使用する方法をまとめます。

 

認証に何を求めているか

まず、手段について論じる前に、認証機能に何を求めるかということについて確認しておきます。

ユーザーが手軽に登録できること

面白そうだなというアプリをApp Storeからダウンロードしても、ユーザー登録にメールアドレスとパスワードを打ち込む必要があり、面倒くさくなって登録しなかったことは僕の実体験としてあります。

App StoreではTouch IDでダウンロードできるので、ストレスなくダウンロードできるはずですが、認証にこのように手間がかかり、さらにそのメールアドレスに確認のメールが行くのでそのリンクをクリックしなければならないなどということがあれば、アプリのメイン機能に辿り着く頃にはユーザーは疲弊していることでしょう。

このような事態を避けるために、ユーザーには最低限の認証機能を提供するべきです。そのためにも、入力というのは面倒な行為なので、できるだけタップのみで進めるべきです。

ユーザーの情報を取得すること

手間がかかるという観点から言えば、ログイン機能自体を使わない方が良いかもしれないと判断されるかもしれませんが、アプリの仕様上そうはならないケースも多いはずです。

というのも、最低限のユーザー情報を獲得したい場合に認証機能が有用だからです。
実際、ユーザーネームやプロフィール画像や自己紹介文などが認証の結果手軽に取得できるので、TwitterやFacebookの認証はとても便利です。

 

SwiftでTwitterのOAuth認証をする方法

ここからが本題です。まず結論を書きますが、iOSでTwitterのOAuth認証をする場合、Twitter Kitを使うのが便利です。以下に理由を示します。

TwitterのOAuthのライブラリを探したところ、二種類あります。

この二つです。
僕は最初はOAuthSwiftを使用していました。OAuthSwiftに関してはこの記事を参考にすれば簡単に実装できると思います。

ただこのライブラリを使った場合は、認証時にSFSafariViewを開いてしまう、という問題がありました。
どういうことかというと、Twitterでログインするというボタンを押したところ、必ずブラウザ版のTwitterによって認証する必要があるということです。
もしもNativeのツイッターアプリをいつも使っている人からしたら、サファリに飛んでしまうとログインするためにユーザー名とパスワードを打ち込まなければならない可能性があり、面倒です。

ブラウザでログインを記憶している場合は良いが、そうでない場合は面倒に感じるので、アプリのメイン機能に行く前に使用自体をやめる可能性があるかもしれません。
もしもいつも使っているネイティブのアプリでも認証ができれば、普段アプリを使っている人たちも連携するボタンをワンタップするだけでログインができるので、その方がいいと思いました。
そしてネイティブのアプリを使っていない人に対してはサファリでログインさせたらいいと思いました。

OAuthSwiftの公式ドキュメントのここにネイティブアプリを使ってOAuth認証をする方法が書いてあるのですが、Twitterの場合は書いていません。
FacebookとDropboxしか書かれていないのです。

でも、どこかで確かにTwitterアプリによるログインをしたことがあるような気がしたので、もう少し調べていると、この記事を見つけました。
この記事によると、Twitter Kitを使うことでアプリがインストールされている場合にはアプリ側に飛んで認証ができるみたいです。

ただ、このライブラリはObjective-Cで書かれていることもあり、少し導入が面倒に思えたことと(実際にはほとんど面倒ではなかった)、ログイン周りの方法を説明している記事も少なかったことから使用するかどうか少し迷いました。
しかし、認証に何を求めるかという最初の説明でも言った通りユーザーの手間を省くことが最重要だと考え、このライブラリを導入しました。

公式のドキュメントを読むと、思っていた以上に手軽で様々なことができるのでオススメのライブラリです。

 

Twitter Kitを実際に使う

Twitter KitはiOSでTwitterのOAuth認証からAPIの使用までを全て利用可能にするライブラリです。Twitter社による正式サポートは2018年の10月末に終了することになりました。これからはオープンソースとして残り続けますが、少し不安はありますね。ただ、OAuth認証やAPIの使用に関する機能は十分にあります。

僕のサンプルプロジェクトをGitHubに上げておきます。

導入

めちゃめちゃ丁寧に書きました。各々についてわかっている人は適当に飛ばして読んでください。よくわからないところがあればググりながら進めてみてください。こちらのドキュメントを参考に書きました。

ツイッターアプリの登録

まずAPIを使うためにツイッターアプリの登録をしてください。WebsiteやCallback URLはなんでも良いです。

登録したら、 SettingsのところにあるEnable Callback Lockingのチェックボックスのチェックを外しておいてください。これを外さないと、This client application’s callback url has been lockedというエラーが出ます。

Carthageでライブラリの導入

僕はCarthageを使ったのでCartfileに

binary "https://ton.twimg.com/syndication/twitterkit/ios/TwitterKit.json"
binary "https://ton.twimg.com/syndication/twitterkit/ios/TwitterCore.json"

と記述し、carthage update –platform iOS というコマンドを実行しました。
続いてTwitterKit.frameworkをGeneralのLinked Frameworks and Librariesに追加しました。

ドキュメントにはTwitterKit.frameworkとTwitterShareExtensionUI.frameworkをGeneralのLinked Frameworks and Librariesに追加するようにと書いてあるのですが、TwitterShareExtensionUI.frameworkは追加しなくても動くかと思います。
また、こちらのイシューがまだオープンだったのでコンソール画面にClass TWTRScribeService is implemented in both. …(中略)… One of the two will be used. Which one is undefined.などの注意が大量に出てしまいますが、無事動くことは確認しているので気にしないことにしましょう。次に

$(SRCROOT)/Carthage/Build/iOS/TwitterCore.framework
$(SRCROOT)/Carthage/Build/iOS/TwitterKit.framework
$(SRCROOT)/Carthage/Build/iOS/TwitterShareExtensionUI.framework

をBuild PhasesのRun ScriptのInput Filesのところに追加しました。

/usr/local/bin/carthage copy-frameworks

も記述してください。

Objective-C Bridging Header

そしてObjective-Cのコードを使えるようにするためにObjective-C Bridging Headerと呼ばれる、{Projectの名前}-Bridging-Header.hというファイルを作成し

#import <TwitterKit/TWTRKit.h>

という文を書き加えた後、Build SettingsからObjective-C Bridging Headerという項目にBridging Headerファイルのパスを記述しましょう。

Twitter Kitのための設定

Twitter Kitを使用するために

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    TWTRTwitter.sharedInstance().start(withConsumerKey:"*****", consumerSecret:"********")
    return true
}

をApp Delegateに追加しました。
そしてinfoのURL schemeにtwitterkit-<consumerKey>というURLを追加したら(<consumerKey>のところは自分のものに置き換える)基本的な設定は完了です。

これは、ディープリンクといい、アプリを開くためのリンクです。これがコールバックURLに設定され、Twitterのアプリで認証を終えた後に自分のアプリに戻ってくることができます。

ログイン機能の実装

ドキュメントのこの部分を見習ってログイン機能を実装しましょう。Twitter Kitを使いたいViewControllerのファイルでimport TwitterKitをした後に、

let logInButton = TWTRLogInButton(logInCompletion: { session, error in
    if (session != nil) {
        print("signed in as \(session.userName)");
    } else {
        print("error: \(error.localizedDescription)");
    }
})
logInButton.center = self.view.center
self.view.addSubview(logInButton)

と記述すればデフォルトのログインボタンを表示させることができますし、

TWTRTwitter.sharedInstance().logIn(completion: { (session, error) in
    if (session != nil) {
        print("signed in as \(session.userName)");
    } else {
        print("error: \(error.localizedDescription)");
    }
})

というメソッドを使えば自分でログインボタンをカスタマイズしたり、自分で作ったメソッドの中でログイン機能を呼び出したりできます。
OAuth認証のことは全てライブラリが行ってくれることがわかると思います。

最後に、

func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
    return TWTRTwitter.sharedInstance().application(app, open: url, options: options)
}

をApp Delegateの中に記述したら、ログイン機能は完成です。
完成したApp Delegateはこんな感じになっているはずです。

本当にアプリに遷移してログインできているのかを実機でテストしたところ、本当に使えました!

上のボタンがデフォルトのログインボタンです。結構おしゃれですね。ちっちゃい下のボタンは押した時にログインメソッドが呼ばれるボタンです。上のボタンと同じ動きをします。実際に押してみると、

上のようにツイッターアプリで連携するかどうかの確認画面が出てきました。ユーザー名やパスワードを入力する必要はありません。連携ボタンを押すと元のアプリに戻ってきます。

実際に連携ボタンが押されて、認証が完了したとしましょう。すると、

TWTRTwitter.sharedInstance().logIn(completion: { (session, error) in
    if (session != nil) {
        print("signed in as \(session.userName)");
    } else {
        print("error: \(error.localizedDescription)");
    }
})

におけるsessionの中に、ユーザーIDやユーザー名やAccess TokenやAccess Token Secretのプロパティが入っているのでこれらを取り出すことができます。僕のサンプルで言うとこの部分です。

以上でログイン機能が完成しました。

アクセストークンとアクセストークンシークレットはUserDefaultsに入れるなどして、ログイン情報を保持しておくと良いでしょう。AuthTokenとAuthTokenSecretから以下のようにセッションをセーブすることで、ログイン状態になり、いちいちTwitterアプリに認証をしにいかなくてもツイートすることができるようになります。

let store = TWTRTwitter.sharedInstance().sessionStore
store.saveSession(withAuthToken: "***", authTokenSecret: "*****", completion: { (session, error) in
    if (session != nil) {
        let composer = TWTRComposer()
        composer.setText("just setting up my Twitter Kit")
        composer.setImage(UIImage(named: "kojilogo"))
        composer.show(from: self) { result in
            if (result == .done) {
                print("Successfully composed Tweet")
            } else {
                print("Cancelled composing")
            }
        }
    } else {
        print("error: \(String(describing: error?.localizedDescription))");
    }
})

ツイート機能の実装は、こちらのドキュメントを参考にしました。TWTRComposerを使えば簡単に実行できることがわかりますね。

テキストや画像をセットすることで、ツイートのポップアップを簡単に表示させることができます。

動画をセットするメソッドは一見なさそうでした。

またこのsessionの中にユーザーのプロフィール画像はないですが、ユーザー情報を取得するメソッドが用意されているので、二度手間ですが、以下のようにprofileImageURLを取得してください。公式ドキュメントではここに書かれています。

let client = TWTRAPIClient()
client.loadUser(withID: "12") { (user, error) -> Void in
    print(user?.profileImageURL ?? "noImage")
}

以上でOAuth認証と基本APIの解説は終わりです。

ログインし直すとうまくcallbackされない時の対処法

2018/11/2 追記.

初回ログインの際はうまく認証が出来たにも関わらず、ログアウトして違うアカウントでログインしようとしたらTwitterの「アプリケーションに戻ります」というWebViewが閉じられるだけでcallbackのURLがうまくTwitterKitに渡らないという挙動に悩まされたので、知見を共有しておきます。

問題としてはログイン状態の保持をUserDefaultsで独自に行っていたのがポイントです。TwitterKitもsessionを保持しているので、このsessionの方もログアウトする必要があるみたいです。

エラーメッセージとしては

Opening link in Safari browser, as the host is not whitelisted

というもので、callback URLの設定がうまく出来ていなかったのかと思いましたがうまくいかず、sessionのログアウトを使用すれば無事ログインし直すことが出来ました。

let sessionStore = TWTRTwitter.sharedInstance().sessionStore
if let session = sessionStore.session() {
    sessionStore.logOutUserID(session.userID)
}

上記のようにsessionStoreからログアウトさせました。参考にしてください。

Twitter Kitを使ってみての感想

認証においてサーバーサイドの実装は不要でした。CallbackURLも使わないし、認証やAPIの使用はレスポンスを実際にパースする必要もなく、ライブラリがやってくれるので便利だと思いました。
メイン機能に関しては独自のメソッドが用意されており、さらにメソッドが用意されていないAPIもJSONをパースすれば使えるので、このライブラリを使えば十分なのではないかと思いました。

何より、スマホにTwitterのアプリがある場合はネイティブアプリで認証できるのが大きかったです。
Twitter認証はブラウザ版しか対応していないというアプリはまだまだ実際にあるので、このライブラリがもっと普及してストレスなくTwitterでログインできるようになったらいいなと思いました。

Pocket