SwiftでFacebookログインと情報取得の覚書

自分が作りたいアプリのFacebookまわりの事をまとめてみました。

#概要
・Facebookログイン（とログアウト）
・ログインしていない場合、ログインボタンとWelcome画面を表示
・ログインした後(もしくは既にログインしている場合)、名前とユーザ画像、Emailに友達リストをMain画面に表示
・というのを、Swiftで書いてみる
#前提
1.環境
・Xcode 6.4
・Facebook SDK for iOS v4.4.0
・Swift version 1.2
・Facebook API Version 2.4

2.事前準備
・Facebook SDK for iOSのダウンロードとインストール
　[ここから](https://developers.facebook.com/docs/ios)ダウンロードして書類フォルダに解凍しておく
・My Appsの登録（[ここから](https://developers.facebook.com/apps/)）
　登録後にもらえるApp IDとアプリ名をメモっておく

3.注意事項
2015/8/5時点、Xcode,FacebookSDK共に英語ページからダウンロードしています。日本語ページだとちょっと古い…
#作業の流れ
1.Xcode 新規プロジェクトの作成
2.プロジェクトへFacebook SDKを追加
3.AppDelegate.swiftの編集
4.Welcome画面の作成
5.Welcome画面にログイン機能をつける
6.Main画面の作成
7.Welcome画面からMain画面への遷移(既にログイン&ログイン成功)
8.Main画面にFBの情報を表示
9.Main画面にログアウト機能をつける


##1. Xcode 新規プロジェクトの作成
1.1 Xcode を起動して Create a new Xcode project をクリック
![スクリーンショット 2015-08-05 20.58.43.png](https://qiita-image-store.s3.amazonaws.com/0/81855/4d3bd4f5-f3a5-480b-59c0-a1994bb5ca86.png)

1.2 iOS|Application → Single View Application を選択して Next
![スクリーンショット 2015-08-05 21.06.10.png](https://qiita-image-store.s3.amazonaws.com/0/81855/dba7b77a-63ef-3966-a49d-924c2b241b15.png)

1.3 プロジェクト情報を入力
![スクリーンショット 2015-08-05 21.20.22.png](https://qiita-image-store.s3.amazonaws.com/0/81855/08016e95-2820-5bd3-fd7e-d5f3b6e01848.png)

> Product Name: FBLoginSample
Organization Name: 所属組織や自分の名前とか(※とりあえず何でも良い)
Organization Identifier: ドメイン名を逆さにしたもの(※とりあえず何でも良い)
Language: Swift
Devices: iPhone
use CoreData: チェックはずす

1.4 プロジェクトを保存する場所を選択して Create

##2. プロジェクトへFacebook SDKを追加
2.1 書類フォルダからFBSDKCoreKit.frameworkを選択して、ナビゲータの一番上あたりにドロップ
![スクリーンショット 2015-08-05 21.27.41.png](https://qiita-image-store.s3.amazonaws.com/0/81855/27bc5c2e-9cba-b90a-9fe5-c29125e73115.png)


2.2 Optionを設定してFinish
![スクリーンショット 2015-08-05 21.28.01.png](https://qiita-image-store.s3.amazonaws.com/0/81855/e78369b3-aad3-2abf-e4e6-d95d754a43c0.png)

> Destination: チェックはずす(とりあえず既定値のまま。チェックしたらプロジェクトフォルダにデータがコピーされる)
Added Forders: Create Groups(とりあえず既定値)
Add to targets: FBloginSample(とりあえず既定値)

ここの設定を変えるとどうなるかとかは、とりあえず脇において既定値のままで。

2.3 FBSDKLoginKit.frameworkも同じようにして追加
![スクリーンショット 2015-08-05 21.28.39.png](https://qiita-image-store.s3.amazonaws.com/0/81855/02882045-75d9-c3cd-5d92-b61ad3e78243.png)

プロジェクトのGeneralタブの一番下、Linked Frameworks and Librariesの＋ボタンから追加する方法もあります。（むしろそちらが正しいやり方かも？？）

2.4 Supporting FilesフォルダのInfo.plistに App ID とアプリ名を登録
![スクリーンショット 2015-08-06 1.17.33.png](https://qiita-image-store.s3.amazonaws.com/0/81855/504cc43c-8576-cc9d-4af7-685495525127.png)


> FacebookAppID : 事前準備でメモった App ID
FacebookDisplayName　:事前準備でメモった アプリ名
URL types | item 0 | URL Schemes | item 0 : fb + App ID

詳しくは[Facebook Developer Center](https://developers.facebook.com/docs/ios/getting-started)の**Configure the .plist**参照


 
##3.AppDelegate.swiftの編集
3.1 FBSDKCoreKitをimportする

```swift:AppDelegate.swift
import FBSDKCoreKit
```
SDK v4.1からimport文だけで使えるようになりました。（一番下のもろもろ参照）

3.2 func applicationとapplicationDidBecomeActiveを編集

```swift:AppDelegate.swift
    func application(application: UIApplication,didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
            return FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
    }
    
    func application(application: UIApplication,openURL url: NSURL,sourceApplication: String?,annotation: AnyObject?) -> Bool {
            return FBSDKApplicationDelegate.sharedInstance().application(application, openURL: url, sourceApplication: sourceApplication, annotation: annotation)
    }

    func applicationDidBecomeActive(application: UIApplication) {
        FBSDKAppEvents.activateApp()
    }

```

おまじないみたいなモノと思いコピペで。


##4.Welcome画面の作成
4.1 ViewController.swiftをWelcomeViewController.swiftにリネーム
  ファイル名の変更にあわせてclass名も変更する
![スクリーンショット 2015-08-06 0.26.17.png](https://qiita-image-store.s3.amazonaws.com/0/81855/c38462e7-2fb3-3f9f-a7ce-b978aefdb811.png)

4.2  Main.Storybordを開き View Controllerを選択する。
右パネル　identity inspectorを選択し、Custom Classを4.1で編集したWelcomeView Controllerに変更する
![スクリーンショット 2015-08-07 15.19.54.png](https://qiita-image-store.s3.amazonaws.com/0/81855/8c49b330-a15e-080f-187f-21a78f4b32af.png)


##5.Welcome画面にログイン機能をつける
5.1.FBSDKCoreKit, FBSDKLoginKitのインポート記述を追記する　

```swift:WelcomeViewController.swift
import FBSDKCoreKit
import FBSDKLoginKit
```

5.2 FBSDKLoginButtonDelegateを追加
5.2.1 WelcomeViewControllerのclass定義にFBSDKLoginButtonDelegateを追加する

```swift:WelcomeViewController.swift
class WelcomeViewController: UIViewController, FBSDKLoginButtonDelegate {
```

5.2.2 func loginButtonとfunc loginButtonDidLogOutを追加する

```swift:WelcomeViewController.swift
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    //ログインボタンが押された時の処理。Facebookの認証とその結果を取得する
    func loginButton(loginButton: FBSDKLoginButton!,didCompleteWithResult
                    result: FBSDKLoginManagerLoginResult!, error: NSError!) {
            println("User Logged In")
            
            if ((error) != nil)
            {
                //エラー処理
            } else if result.isCancelled {
                //キャンセルされた時
            } else {
                //必要な情報が取れていることを確認(今回はemail必須)
                if result.grantedPermissions.contains("email")
                {
                // 次の画面に遷移（後で）
                }
            }
    }
    
    //ログアウトボタンが押された時の処理
    func loginButtonDidLogOut(loginButton: FBSDKLoginButton!) {
        println("User Logged Out")
    }
```


5.3 ログイン状態のチェック
func viewDidAppearにログイン状態の確認処理を書く。
　・ログイン状態の場合→次の画面(この次に作るメイン画面）を表示
　・未ログイン状態→ログイン画面を表示して、Facebook認証でログインする
viewDidLoadではないです。viewDidLoadに書くとログインをスキップして次を表示する際にエラー（表示終わっていないのに次の画面呼ぶのはダメよ…みたいな）がでるので。

```swift:WelcomeViewController.swift
    override func viewDidAppear(animated: Bool) {
        if (FBSDKAccessToken.currentAccessToken() != nil) {
            println("User Already Logged In")
            //後で既にログインしていた場合の処理（メイン画面へ遷移）を書く
       } else {
            println("User not Logged In")
            let loginView : FBSDKLoginButton = FBSDKLoginButton()
            self.view.addSubview(loginView)
            loginView.center = self.view.center
            loginView.readPermissions = ["public_profile", "email", "user_friends"]
            loginView.delegate = self
        }
    }
```

5.4 ここで試しに起動してみる。
Login with Facebookボタンが表示される→Facebookログイン画面が表示される→(メールアドレスとパスワードを入力する。開発登録していないユーザで入るとエラーになる)→情報へのアクセス許可画面→(ログイン)→ログアウトボタンが表示される→ログアウト→はじめに戻る

![スクリーンショット 2015-08-10 1.11.19.jpg](https://qiita-image-store.s3.amazonaws.com/0/81855/d4556a51-8424-5c0e-e1d8-11beb01e6970.jpeg)




##6.Main画面の作成

6.1 ログインした後、遷移させる画面を作る。Main.Storybordを開き、ViewControllerをキャンバス上にドラッグ＆ドロップする
![スクリーンショット 2015-08-10 1.56.15.png](https://qiita-image-store.s3.amazonaws.com/0/81855/feb14dc6-d8b7-e8f2-fd3d-194c46e64576.png)


6.2 作ったViewControllerにFacebookから取得した情報を表示するコンポーネントを追加。
![スクリーンショット 2015-08-10 1.52.10.png](https://qiita-image-store.s3.amazonaws.com/0/81855/5ffbc59f-4f11-994e-e36f-f5fc80c56343.png)

Identity Inspectorを選択しRestracion IDを入力する

> Label : Welcome（固定値）
UIImageView : userImage (プロフィール写真用)
Label : currentUserName(名前を表示する用)
Label : currentUserEmail(emailを表示する用)
Button : Logout(ログアウトボタン)

##7.Welcome画面からMain画面への遷移(既にログイン&ログイン成功)
7.1 segueを作る
WelcomeViewControllerを選択し、Controlキーを押しながらMain画面をクリックする（開発を始めた頃、Commandキーを押しながらSegueを作ろうとして指定された通りに線が出ないんだけど！と悩んだのは内緒です）
![スクリーンショット 2015-08-11 0.07.37.png](https://qiita-image-store.s3.amazonaws.com/0/81855/cd41e5e8-53a4-2266-ca17-a24380a70037.png)

Segueの選択画面が表示されるので present modally を選択する
![スクリーンショット 2015-08-11 0.08.02.png](https://qiita-image-store.s3.amazonaws.com/0/81855/34f4a10d-297b-1511-8c92-b5e1a8371982.png)

SegueのAttiributes Inspectorを選択し、Identifierを入力する
![スクリーンショット 2015-08-11 0.17.31.png](https://qiita-image-store.s3.amazonaws.com/0/81855/635b613f-aea9-4223-6cf6-0d40a12dcbcc.png)
> Identifier : ShowMain


7.2 Welcome画面からの遷移を記述する。
起動した時に既にログインしていた場合と、ログイン画面から問題なく認証できた場合に飛ぶ部分を書く。具体的には、5.2.2の「// 次の画面に遷移（後で）」と、5.3の「//後で既にログインしていた場合の処理（メイン画面へ遷移）を書く」のコメント行をperformSegueWithIdentifierで遷移するように書き換える

```swift:WelcomeViewController.swift
    override func viewDidAppear(animated: Bool) {
        if (FBSDKAccessToken.currentAccessToken() != nil) {
            println("User Already Logged In")
            self.performSegueWithIdentifier("showMain", sender: self)
...
```


```swift:WelcomeViewController.swift
...
    if result.grantedPermissions.contains("email"){
        self.performSegueWithIdentifier("showMain", sender: self)
    }
...         
```

##8.Main画面にFBの情報を表示する
8.1 MainViewController.swiftを新規作成し、6.1で作ったViewControllerに紐付ける
8.2 FBSDKCoreKit, FBSDKLoginKitのインポート記述を追記する　

```swift:MainViewController.swift
import FBSDKCoreKit
import FBSDKLoginKit
```

8.3 写真用のイメージと名前とemailをMainViewController.swiftで使えるようにする。
Main.StoryboardのUIImageを選択し、Controlキーを押しながらMainViewController.Swiftの上にドロップ。
![スクリーンショット 2015-08-11 1.06.26.png](https://qiita-image-store.s3.amazonaws.com/0/81855/33583c04-7121-112b-5738-435948c9bd2f.png)

ダイアログが表示されるので、Connectionを設定する。名前とemailも同様に。
![スクリーンショット 2015-08-11 1.07.06.png](https://qiita-image-store.s3.amazonaws.com/0/81855/844973ab-2265-9ab8-f512-55efb115c56c.png)


結果として、この３行がMainViewController.swiftに追加される

```swift:MainViewController.swift
    @IBOutlet weak var currentUserName: UILabel!
    @IBOutlet weak var currentUserEmail: UILabel!
    @IBOutlet weak var userImage: UIImageView!
    
```

8.4 FBから取得してきたデータを格納するフィールドを宣言

```swift:MainViewController.swift
    @IBOutlet weak var currentUserName: UILabel!
    @IBOutlet weak var currentUserEmail: UILabel!
    @IBOutlet weak var userImage: UIImageView!
    
    var userProfile : NSDictionary!

```

8.5 画面表示時(viewDidLoad)に、FBからデータ取ってきて表示する（returnUserData）

```swift:MainViewController.swift
    override func viewDidLoad() {
        super.viewDidLoad()
        returnUserData()
      }

```

```swift:MainViewController.swift
   func returnUserData()
    {
        let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me",
            parameters: ["fields": "id, name, first_name, last_name, picture.type(large), email"])
            graphRequest.startWithCompletionHandler({ (connection, result, error) -> Void in
            if ((error) != nil)
            {
                // エラー処理
                println("Error: \(error)")
            }
            else
            {
                // プロフィール情報をディクショナリに入れる
                self.userProfile = result as! NSDictionary
                println(self.userProfile)

                // プロフィール画像の取得（よくあるように角を丸くする）
                let profileImageURL : String = self.userProfile.objectForKey("picture")?.objectForKey("data")?.objectForKey("url") as! String
                var profileImage = UIImage(data: NSData(contentsOfURL: NSURL(string: profileImageURL)!)!)
                self.userImage.clipsToBounds = true
                self.userImage.layer.cornerRadius = 60
                self.userImage.image = self.trimPicture(profileImage!)

                //名前とemail
                
                self.currentUserName.text = self.userProfile.objectForKey("name") as? String
                self.currentUserEmail.text = self.userProfile.objectForKey("email") as? String

                }
        })
    
    }
    func trimPicture(rawPic:UIImage) -> UIImage {
        var rawImageW = rawPic.size.width
        var rawImageH = rawPic.size.height
            
        var posX = (rawImageW - 200) / 2
        var posY = (rawImageH - 200) / 2
        var trimArea : CGRect = CGRectMake(posX, posY, 200, 200)
            
        var rawImageRef:CGImageRef = rawPic.CGImage
        var trimmedImageRef = CGImageCreateWithImageInRect(rawImageRef, trimArea)
        var trimmedImage : UIImage = UIImage(CGImage : trimmedImageRef)!
        return trimmedImage
    }

```
Facebook API Version 2.３までは、parametersを細かく指定しなくてもemailは取得できていたみたいですが、2.4から個別に書かないとダメになりました。IDと名前しか戻ってきません。

##9.Main画面にログアウト機能をつける（成功したらWelcome画面に戻る)
Main.StoryboardのLogoutボタンを選択し、Controlキーを押しながらMainViewController.Swiftの上にドロップ。ダイアログが表示されるので、Accionを設定する。

![スクリーンショット 2015-08-11 1.32.25.png](https://qiita-image-store.s3.amazonaws.com/0/81855/24cee330-8f8a-f6d3-d4c7-840ae5d4233c.png)

connectionの設定の部分をoutletからActionに変更する。これも引っかかりました…。

```swift:MainViewController.swift

    @IBAction func logout(sender: AnyObject) {
        let loginManager : FBSDKLoginManager = FBSDKLoginManager()
        loginManager.logOut()
        self.dismissViewControllerAnimated(true, completion: nil)
    }
```    

##10.完成！


#もろもろ
・最新のFacebook SDK v4.1からSwiftがネイティブサポートされ、Bridging headerが不要になりました。(＝swiftに直接使うライブラリをimportすればOK)

・Facebookの仕様変更で、アプリを利用している友達しか一覧が取れません。多少の裏技はあるみたいですが、正攻法では難しいみたいです。確認するためには開発仲間を巻き込みましょう……。[Sunday In The Park](http://snowadays.jp/2014/08/2983)さんの記事が大変参考になりました。で、作っても友達が表示されないので今回は実装をスキップしました。

・作ったものは[Github](https://github.com/nodagiri/FBLoginSample)に置きました。実行前にFacebookSDKをプロジェクトに追加してください。

・間違ってるとか、こっちの方が良いんじゃ？などの指摘ツッコミ大歓迎。画面がチラチラするのが気になるので誰か良い方法教えてくださいm(__)m