mitsu's techlog

Customer Reliability Engineerになりました。備忘録も兼ねて技術ネタを適当に。技術以外はこっち→http://mitsu9.hatenablog.com/

SwiftでFacebookのユーザー認証からデータ取得まで(Facebook SDK)

SNSとの連携ということで代表的なFacebookの認証からデータ取得まで実装しました。 FacebookはSocialFrameworkとFacebook SDKを使う2種類の方法があるのですが、今回はFacebook SDKを使って実装してみました。

SDKの導入

以下のリンクの内容に沿ってSDKを導入します。 行ったことは以下の5項目です。

  1. SDKのダウンロード
  2. Facebook Appの作成 (App ID等の取得)
  3. ProjectにFacebookSDKを追加
  4. plistファイルにApp IDなどの情報を追加
  5. Application Delegateに必要なコードを追加

Facebook SDK for iOS - Getting Started

ユーザー認証

  1. ログインボタンの設置

SDKの中にログインボタンがあるので、そちらを利用してボタンを設置します。 ボタンをViewに追加するだけで認証に関する処理は全てSDK側でしてくれるので非常に楽です。

カスタムで作りたい方はボタンタップ後の処理を数行書く必要があります。

// 認証に必要なSDKをimportする
import FBSDKCoreKit
import FBSDKLoginKit

class ViewController: UIViewController, FBSDKLoginButtonDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        let loginButton = FBSDKLoginButton() // ボタンの作成
        loginButton.center = self.view.center // 位置をcenterに設定
        loginButton.delegate = self // 認証後の処理のためにdelegateを設定
        loginButton.readPermissions = ["public_profile", "email", "user_friends"] // 欲しいデータに合わせてpermissionを設定
        self.view.addSubview(loginButton) // viewにボタンを追加
    }
}
  1. 認証後の処理

delegateを設定しておくことで認証後にメソッドが呼び出されます。 FBSDKLoginButtonDelegateプロトコルで定義されているメソッドは、

  • func loginButton(loginButton: FBSDKLoginButton!, didCompleteWithResult result: FBSDKLoginManagerLoginResult!, error: NSError!)
  • func loginButtonDidLogOut(loginButton: FBSDKLoginButton!)

になります。 ログイン時とログアウト時に呼ばれる2種類のメソッドです。

これらはサンプルを利用して以下のように実装しました。

    func loginButton(loginButton: FBSDKLoginButton!, didCompleteWithResult result: FBSDKLoginManagerLoginResult!, error: NSError!){
        println("User Logged In")
        
        if ((error) != nil)
        {
            // Process error
        }
        else if result.isCancelled {
            // Handle cancellations
        }
        else
        {
            // If you ask for multiple permissions at once, you
            // should check if specific permissions missing
            if result.grantedPermissions.contains("email")
            {
                // Do work
            }

            // ログイン後の処理はここに書く
        }
    }
    
    func loginButtonDidLogOut(loginButton: FBSDKLoginButton!) {
        println("User Logged Out")
    }

Facebook SDKを用いたとき認証に必要なコードはこれだけになります。 ほとんどサンプルのコピーなので、認証に関しては問題無く実装できると思います。

ユーザー情報の取得

ユーザーの情報を取得するにはFBSDKGraphRequestのインスタンスを作成し、データ取得後の処理を引数にstartWithCompletionHandlerを呼び出します。

FBSDKGraphRequest初期化時にparametersを指定することで必要な情報のみを取得することができます。 以下のコードでは(おそらく)全ての情報を取得しています。

取得できる情報は認証時にreadPermissionsに設定した項目によって変化します。 今回はreadPermissionsを["public_profile", "email", "user_friends"]に設定しているので、(おそらく)全ての情報がとれる権限になっています。

func returnUserData() {
    
    let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me",
        parameters: ["fields": "id,email,gender,link,locale,name,timezone,updated_time,verified,last_name,first_name,middle_name"])
    graphRequest.startWithCompletionHandler({ (connection, result, error) -> Void in
        
        if ((error) != nil)
        {
            // Process error
            println("Error: \(error)")
        }
        else
        {
            println("fetched user: \(result)")

            // 個々の情報を取得したいときはこんな感じ
            let userName : NSString = result.valueForKey("name") as! NSString
            println("User Name is: \(userName)")
        }
    })
}

出力は以下のようになります。

fetched user: {
    email = "mail@mail";
    "first_name" = firstname;
    gender = male;
    id = randomnumber;
    "last_name" = lastname;
    link = "https://www.facebook.com/app_scoped_user_id/user_id/";
    locale = "ja";
    name = "firstname lastname";
    timezone = "-5";
    "updated_time" = "2015-07-18T03:34:22+0000";
    verified = 1;
}
User Name is: firstname lastname

友達のリストの取得

現在Facebookの友達のリストをとるAPIはないのですが、taggable_friendsというAPIを利用して友達の情報を取得することができます。

taggable_friendsを利用するとデフォルトでは25人分の情報と次の友達の情報を取得するための情報が返ってきます。(以下に例があります)

実際にアプリを開発する際はスクロールなどに合わせてAPIから情報を取得していくことになると思うのですが、今回は面倒なので全てのfriendsを一回で取得しようと思います。 そのためにパラメーターにlimitを追加して、友達の数をlimitに設定します。

この友達の数はfriendsというAPIから取得できるので、friendsで友達の数を取得し、その後全ての友達を取得するように実装しました。

ちなみに、実際に実行してみたところfriendsで取得した友達の数とtaggable_friendsで取得した友達の数に若干の差がありました(僕の場合877と859)。 taggable_friendsという名前からわかるようにtaggableでない友達は取得できないので、全ての友達は取得できません。 しかし、taggableでない友達は少ないと思うのであまり困ることはないかと思います。 (それにしてもtaggableでない友達ってどういう人?わかる方いたら教えてください。)

taggable_friendsのレスポンス↓

{
  "data": [
    {
      "id": "XXXXXXX",
      "name": "hoge hoge",
      "picture": {
        "data": {
          "is_silhouette": false,
          "url": "url"
        }
      }
    },
    {
      "id": "XXXXX",
      "name": "huga huga",
      "picture": {
        "data": {
          "is_silhouette": false,
          "url": "url"
        }
      }
    }
],
  "paging": {
    "cursors": {
      "before": "XXXXXXXXXX",
      "after": "XXXXXXXXXX"
    },
    "next": "https://graph.facebook.com/v2.4/854530921331223/taggable_friends?access_token=ACCESS_TOKEN&pretty=0&limit=25&after=XXXXXXXXXXXX"
  }
}

友達のリストを取得するメソッド

func friendsList() {
    
    let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me/friends", parameters: nil)
    graphRequest.startWithCompletionHandler( { (connection, result, error) -> Void in
        
        if ((error) != nil)
        {
            // Process error
            println("Error: \(error)")
            return
        }
        
        //  友達の数を取得し次のリクエストのlimitに利用
        let summary = result.valueForKey("summary") as! NSDictionary
        let counts = summary.valueForKey("total_count") as! NSNumber
        
        let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me/taggable_friends", parameters: ["fields": "id,name,picture", "limit": "\(counts)"])
        graphRequest.startWithCompletionHandler( { (connection, result, error) -> Void in
            
            if ((error) != nil)
            {
                // Process error
                println("Error: \(error)")
                return
            }
            else
            {
                // 友達を一人ずつ出力
                let friends = result.valueForKey("data") as! NSArray
                var count = 1
                if let array = friends as? [NSDictionary] {
                    for friend : NSDictionary in array {
                        let name = friend.valueForKey("name") as! NSString
                        println("\(count) \(name)")
                        count++
                    }
                }
            }  
        })  
    })
}

補足

Facebookの認証の権限はユーザー側で管理することができます。 具体的にはreadPermissionsを["public_profile", "email", "user_friends"]と設定していても、ユーザーによって実際は["public_profile"]だけの権限になっている場合があります。 このことによって、NSDictionaryで返ってくるデータに対してKeyが存在しないことがあり得るので、実際にアプリを開発する際はそのことを意識してOptional型を適切に利用する必要があります。 権限を持っているつもりでOptional Valueに対してforce wrappingしてしまうとアプリが落ちることになるので気をつけましょう。

(※上記のコードは一切このことについて気にしていません。)

最後に

認証は簡単にできたのですが、ドキュメントがよくわからずデータ取得に手こずりました。 今回はデータを取得することしかしていないので、次はポストやシェアができるようにしたいと思います。

全体のコードはGistにあるので参考にしたい方はどうぞ。

The demonstration of how to use Facebook SDK

参考サイト

Facebook SDK for iOS - Getting Started

Facebook API v2.0で、フレンド数やフレンド一覧を取得する方法 | Sunday In The Park

https://developers.facebook.com/tools/explorer/