LoginSignup
8
7

More than 5 years have passed since last update.

NCMBでPodのuse_frameworks!有効時にFacebookSDKが入れられないことを解決した

Posted at

NCMBとFacebookSDK

NCMB

NCMBとは、ニフティクラウド mobile backendのことで、
ニフティクラウドが提供するmBaaSです。SNS連携もサポートされ、Facebookログインが用意されています。
http://mb.cloud.nifty.com/

FacebookSDK

iOSアプリでFacebookログインを使うには、通常はドキュメントにあるFacebookSDKが必要です。
http://mb.cloud.nifty.com/doc/current/sns/facebook_ios.html#Facebook SDKのインストール

use_frameworks!

CocoaPodsでは0.36からuse_frameworks!構文がサポートされました。
Swift 対応版 CocoaPods を使う - Qiita
http://qiita.com/taketin/items/8264aeebc5a626c6d48f
Swiftで書かれたライブラリをCocoaPods経由でインストールするには、このオプションが必須となりました。

問題

NCMBもFacebookSDKもそれぞれuse_frameworks!を指定してpod installまでは正常に入ります。
しかし、コンパイルすると以下のIssueにあるようにエラーが出てしまっていました。
https://github.com/NIFTYCloud-mbaas/UserCommunity/issues/299
Issueを見る限りずっと解決されていない問題のようでしたが、use_frameworks!を外すと非常に面倒なので、use_frameworks!がオンのままの方法を模索しました。

解決方法

ライブラリ化とサンプルアプリ

以下のレポジトリにライブラリ化し、サンプルアプリが動くようにしました
https://github.com/hiromi2424/NCMB-Facebook-use-framework

使い方

import NCMB_Facebook_use_framework

NCMB.setApplicationKey("APPキー", clientKey: "クライアントキー")
// ログインする
NCMBFacebookLogin(appId: "FacebookAppId").loginWithFacebook({ (user, account) in
    // ここでアカウント情報からユーザーに保存したい情報を設定する
    user.setObject(account.valueForKeyPath("properties.ACUIDisplayUsername"), forKey: "mailAddress")
}).then({ (user) -> Void in
    // userにFacebookログインで作成or取得されたNCMBUserが返ってくる
    self.user = user
}).always({
    // 成功・エラーに関わらず実行されるブロック
    self.loginButton.enabled = true
    self.refresh()
}).error({ (error) -> Void in
    // エラー表示はここでする
    self.alert(String(error))
})

サンプルアプリ実行

git clone https://github.com/hiromi2424/NCMB-Facebook-use-framework
cd NCMB-Facebook-use-framework/Example
pod install

上記実行後、XCodeでExample/NCMB-Facebook-use-framework.xcworkspaceを開きます。
スキーマをExampleに切り替えてビルドすることで動作を確認できます。
スクリーンショット 2016-04-30 14.31.25.png

※ pod tryで実行できるようにしたかったのですが、githubレポジトリを指定する方法はPrivate Specを用意する必要がありました。時間がある時にやってみます
http://stackoverflow.com/questions/25759170/how-to-add-a-private-cocoapod-as-a-dependency-in-another-pod-podspec-file

※ 前提の設定として、NCMBの管理画面で以下の設定が必要です
Facebook設定.png

Social Framework

SwiftでFacebook連携しよう! - Qiita iOS6で追加されたSocial.frameworkの使い方入門 - Qiita
http://qiita.com/happy_ryo/items/fef90627418b0a39a866

Social Frameworkを使い、Facebookの情報を取得します。

NCMBFacebookLogin.swift

    // FacebookアカウントをiOSのAccountsフレームワークを使って取得する
    // 取得できればACAccountのインスタンスをプロミス経由で返す
    // Facebookアカウントが複数ある場合は考慮していない(おそらくそのような状況は無い)
    public func getFacebookAccount() -> Promise<(ACAccount, ACAccountCredential)> {
        return Promise<(ACAccount, ACAccountCredential)>(resolvers: { (fulfill, reject) in
            let accountStore = ACAccountStore()
            let accountType = accountStore.accountTypeWithAccountTypeIdentifier(ACAccountTypeIdentifierFacebook)
            var options = [String: AnyObject]()
            options[ACFacebookAppIdKey] = appId
            options[ACFacebookPermissionsKey] = permissionsKey
            options[ACFacebookAudienceKey] = audienceKey

            accountStore.requestAccessToAccountsWithType(accountType, options: options, completion: { (granted, error) in
                if granted {
                    print("Success to get facebook account")
                    let accounts: NSArray = accountStore.accountsWithAccountType(accountType)
                    if accounts.count >= 1 {
                        let account: ACAccount = accounts.lastObject as! ACAccount
                        fulfill((account, account.credential))
                    } else {
                        reject(NSError(domain: "AccountNotFound", code: 403, userInfo: nil))
                    }
                } else {
                    if error.domain == "" {

                    }
                    print("facebook login error:")
                    print(error)
                    reject(error)
                }
            })
        })
    }

account.credentialを参照していないと、Promiseのブロックを抜けた時credentialがnilになる現象が発生してはまりました(GC関連だと思いますが、追いきれていません)

ここで取得した情報を元にFacebookログインすれば大丈夫そうに思えますが、SocialFrameworkで取得したFacebookIDだとNCMBにOAuth authentication errorと言われてしまいました。
デバッグの結果、Graph APIエクスプローラで取得したトークンを使ってIDを取得すると、SocialFrameworkで取得したFacebookIDと異なっていました。
https://developers.facebook.com/tools/explorer
アプリごとにFacebookIDが割り振られる仕様になったことが原因かと思います(間違っていたら教えて下さい)
以下のようにして直接GraphAPIを叩いて正しいほうのFacebookIDを取得します。

NCMBFacebookLogin.swift

    // AccountsフレームワークのFacebookIDが認証用に使えないことがあるので、GraphAPIを使ってグローバルに使えるIDを取得する
    public func getFacebookActualId(oauthToken: String) -> Promise<String> {
        let url = "https://graph.facebook.com/v2.6/me?access_token=\(oauthToken)"
        let req = Alamofire.request(.GET, url)
        return Promise<String>(resolvers: { (fulfill, reject) in
            req.responseSwiftyJSON({ (request, response, json, error) in
                if error == nil {
                    fulfill(json["id"].stringValue)
                } else {
                    reject(error!)
                }
            })
        })
    }

取得した情報を元にNCMBUserのFacebookログイン機能を使ってユーザーを取得することで、ログインすることができました。

まとめ

要点は2点です。

  • FacebookSDKが使えない時はSocialフレームワークを使う
  • NCMBのFacebookログインに渡すIDを取得する為にGraphAPIで正しいFacebookIDを取得する

use_frameworks!を公式で対応してくれないのは辛かったのですが、これでFacebookログインはなんとかなりそうです!

8
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
7