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に切り替えてビルドすることで動作を確認できます。
※ 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の管理画面で以下の設定が必要です
Social Framework
SwiftでFacebook連携しよう! - Qiita iOS6で追加されたSocial.frameworkの使い方入門 - Qiita
http://qiita.com/happy_ryo/items/fef90627418b0a39a866
Social Frameworkを使い、Facebookの情報を取得します。
// 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を取得します。
// 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ログインはなんとかなりそうです!