Twitter APIを使ってログイン・ツイート取得をしよう


##Loginの処理を作ろう

前回作ったTwitterサンプルを利用して、実際のアカウントでログイン処理を実装しよう

こちらが前回作ったもの

![twitter_sample_image.jpg](https://qiita-image-store.s3.amazonaws.com/0/35442/7ce0f346-fbbd-89e8-d8d1-f2553f196c2a.jpeg)


これをベースに実際のログイン機能を加えていきます。

##事前に以下の準備しておきましょう

* 第３回授業までのプロジェクト用意
* Twitterアカウント作成
* できればiPhoneの設定からTwitterアカウントを設定

###3回目までの授業プロジェクトを持っていない人は

下記のGitHubのURLからファイルを落としておきましょう。
https://github.com/hayate1996/twitter_sample_03

ファイルのダウンロードは、右下の"Download ZIP"から落とすことができます。

![Screen Shot 2015-09-04 at 1.00.53 PM.png](https://qiita-image-store.s3.amazonaws.com/0/35442/cd2ffd45-b9bd-aeaa-8162-2d4f8838143c.png)



###Twitterアカウントをまだ作ってない人は

Twitterアカウントを持っていない人は、以下の手順で作成しましょう。
Twitterの公式ページへアクセスして、新規作成をクリックします。
![Screen Shot 2015-08-26 at 2.49.57 AM.png](https://qiita-image-store.s3.amazonaws.com/0/35442/13f28cc6-2c92-2c51-41f6-db510acb4cb3.png)

新規作成をクリックすると下記の画面があるので、登録するアカウント名や自分のメールアドレスを入力しましょう。
※ 下記は記入例です。

![Screen Shot 2015-08-26 at 3.02.10 AM.png](https://qiita-image-store.s3.amazonaws.com/0/35442/f82db3d0-13c3-1ca0-6b67-d02ca618908e.png)


すると、アカウントが作成されて認証用のメールアドレスが届くので認証すれば終わりです。電話番号の入力はスキップできます。

登録が終わったら、レコメンドなどを利用して有名人や友達のアカウントをフォローしておきましょう。

##iPhoneに登録したアカウントを表示・選択しよう

それでは、iPhoneに登録したアカウントのデータ取得をする部分の実装をしていきます。
アカウントのデータ取得は、基本的にプログラムを書いて実装していきます。

アカウントのデータを取得するには、Twitterが提供しているAPIを利用します。APIを利用するために、iOSで提供しているAccountフレームワークとSocialフレームワークの２つをプロジェクトに追加します。

###フレームワーク追加

1: ナビゲーションエリアで一番上に表示されているプロジェクトファイルを選択します。

![Screen Shot 2015-08-26 at 3.40.57 AM.png](https://qiita-image-store.s3.amazonaws.com/0/35442/5cf337a1-f203-fb0c-540e-0608786fbac3.png)

2: コーディングエリアのタブから"Build Phase"を選択します。

![Screen Shot 2015-08-26 at 3.41.09 AM.png](https://qiita-image-store.s3.amazonaws.com/0/35442/1912bf2d-d6c7-2351-f7d6-bb998d420ce9.png)


3: コーディングエリアに表示されたリストの"Link Binary With Libraries"の▶︎をクリックして展開します。

![Screen Shot 2015-08-26 at 3.41.29 AM.png](https://qiita-image-store.s3.amazonaws.com/0/35442/f3adb7c0-f9fa-31f9-a737-333412278ad1.png)

4: "+"ボタンからフレームワークを追加します。

![Screen Shot 2015-08-26 at 3.41.36 AM.png](https://qiita-image-store.s3.amazonaws.com/0/35442/0b38c9dc-45b9-c503-1d96-564c53d53dee.png)

5: 検索バーに"Account"と入力してAccount.Frameworkを選択した状態で"Add"をします。

![Screen Shot 2015-08-26 at 3.42.03 AM.png](https://qiita-image-store.s3.amazonaws.com/0/35442/5d67095a-1bc5-2054-4db9-23cb2e9b141d.png)

6: すると、"Link Binary With Libraries"にAccount.Frameworkが追加されています。同じ手順でSocial.Frameworkも追加をしましょう。

![Screen Shot 2015-08-26 at 3.42.21 AM.png](https://qiita-image-store.s3.amazonaws.com/0/35442/b6540d83-2a1c-6b32-9411-dcef6db6fd7f.png)

###コーディング

先ほどプロジェクトに追加したAccountフレームワークをViewControllerで読み込みましょう。読み込むには"import"を使います。

```swift
import UIKit
import Accounts   // 追加

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
/*
    省略
    ...
*/
}
```

デバイスに登録したTwitterアカウントを取得して表示するための変数を定義します。accountsListは削除します。

```swift
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    var accountStore = ACAccountStore()   // 追加
    var twAccount = ACAccount()           // 追加
    var accounts = [ACAccount]()          // 追加

/*
    省略
    ...
*/
}
```

viewDidLoadからaccountsListの初期化を削除します。

```swift
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    /* 省略 */

    // 変更
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    /* 省略 */
}
```

デバイスからTwitterのアカウント情報を読み込んで、アカウントのリストを表示します。

```swift
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    /* 省略 */
    @IBAction func tappedLoginButton(sender: AnyObject) {
        /* 省略 */
    }

    // 追加
    /* iPhoneに設定したTwitterアカウントの情報を取得する */
    func getTwitterAccountsFromDevice(){
        let accountType = accountStore.accountTypeWithAccountTypeIdentifier(ACAccountTypeIdentifierTwitter)
        accountStore.requestAccessToAccountsWithType(accountType, options: nil) { (granted:Bool, aError:NSError?) -> Void in
            
            // アカウント取得に失敗したとき
            if let error = aError {
                println("Error! - \(error)")
                return;
            }
            
            // アカウント情報へのアクセス権限がない時
            if !granted {
                println("Cannot access to account data")
                return;
            }
            
            // アカウント情報の取得に成功
            self.accounts = self.accountStore.accountsWithAccountType(accountType) as! [ACAccount]
            self.showAndSelectTwitterAccountWithSelectionSheets()
        }
    }
    /* 省略 */
}
```

ログインするアカウントを選択した後のタイムライン画面への遷移処理を追加します。

```swift
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    /* 省略 */
    func selectTwitterAccountsFromDevice(){
         /* 省略 */
    }

    // 追加
    /* iPhoneに設定したTwitterアカウントの選択画面を表示する */
    func showAndSelectTwitterAccountWithSelectionSheets() {
        
        // アクションシートの設定
        var alertController = UIAlertController(title: "Select Account", message: "Please select twitter account", preferredStyle: .ActionSheet)
        
        for account in accounts {
            
            alertController.addAction(UIAlertAction(title: account.username, style: .Default, handler: { (action) -> Void in
                // 選択したアカウントをtwAccountに保存
                self.twAccount = account
                self.performSegueWithIdentifier("segueTimelineViewController", sender: nil)
            }))
            
        }
        
        // キャンセルボタンは何もせずにアクションシートを閉じる
        let CanceledAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
        alertController.addAction(CanceledAction)

        // アクションシート表示
        self.presentViewController(alertController, animated: true, completion: nil)
    }
}
```

TimelineViewControllerに選択したアカウントを渡すための処理を追加します。

```swift
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    /* 省略 */

    /* アカウントを選択したあと、タイムライン画面へ遷移する処理 */
    func showAndSelectTwitterAccountWithSelectionSheets(accounts: [ACAccount]) {
        /* 省略 */
    }
}
```

Loginボタンを押したときメソッド(tappedLoginButton)の処理を変更します。

```swift
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    /* 省略 */

    //  変更
    @IBAction func tappedLoginButton(sender: AnyObject) {
         self.selectTwitterAccountsFromDevice()
    }
    /* 省略 */
}
```

それぞれを追加したら、シミュレーターを起動して実行してみましょう。

![comp_01.png](https://qiita-image-store.s3.amazonaws.com/0/35442/4fd9a783-25f7-6149-8f0e-07cc47a6ee01.png)


##解説

####フレームワーク
今回、SocialとAccountの２つのフレームワークを利用してTwitterアカウントとタイムラインの取得を行いました。では、Frameworkとはなんでしょう？

フレームワークとは、特定の機能を使いやすくしたり、必要な機能を提供するプログラムをまとめたものです。SocialとAccountのフレームワークは、Social機能を使いやすくしてくれます。

_例えば、マップのフレームワークでは_

* _"GPSを使って座標を取得する"_
* _"現在地から目的地へのルートを取得する"_
* _"取得した座標をマップに表示する"_
* _"マップのUI"などを提供します。_

などを提供して、実装を助けてくれています。

##タイムラインを取得して表示しよう

TimelineViewControllerにタイムラインを取得する処理を追加しましょう。

先ほどプロジェクトに追加したSocialフレームワークをTimelineViewControllerで読み込みましょう。読み込むには"import"を使います。

```swift
import UIKit
import Social     // 追加
import Accounts   // 追加

class TimelineViewController: UITableViewController, UITableViewDelegate, UITableViewDataSource {
    let cellIdentifier = "tweetCell"
    var tweets = []   // 変更

    var twAccount = ACAccount()  // 追加

    override func viewDidLoad() {
        super.viewDidLoad()
        self.navigationItem.title = "Timeline"
        fetchTimeline()  // 追加
        tableView.contentOffset.y = -self.refreshControl!.frame.size.height //追加
        //  tweetsの初期化は削除
    }
/*
    省略
    ...
*/
}
```

TimelineViewControllerに選択したアカウントを渡すための処理をViewControllerに追加しましょう。

```swift
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    /* 省略 */

    // TimelineViewControllerを表示する際に選択したアカウントを渡す
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if segue.identifier == "segueTimelineViewController" {
            var vc = segue.destinationViewController as! TimelineViewController
            vc.twAccount = self.twAccount
        }
    }
}
```

タイムラインを取得するメソッドfetchTimeline()を追加します。

```swift
class TimelineViewController: UITableViewController, UITableViewDelegate, UITableViewDataSource {

    /* 省略 */

    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        self.performSegueWithIdentifier("showTimelineDetailViewController", sender: nil)
    }

    //  追加
    //  Twitter APIを使ってタイムラインを取得しtweetsに保存する
    func fetchTimeline() {
        let URL = NSURL(string: "https://api.twitter.com/1.1/statuses/home_timeline.json")
        
        let request = SLRequest(forServiceType: SLServiceTypeTwitter, requestMethod: .GET, URL: URL, parameters: nil)
        request.account = twAccount
        
        UIApplication.sharedApplication().networkActivityIndicatorVisible = true
        
        request.performRequestWithHandler { (data, response, error:NSError?) -> Void in
            UIApplication.sharedApplication().networkActivityIndicatorVisible = false
            
            if error != nil {
                println("Fetching Error: \(error)")
                return;
            }
            
            self.tweets = NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments, error: nil) as! NSArray
            dispatch_async(dispatch_get_main_queue(), {
                self.tableView.reloadData()
            })
        }
    }
}
```

tableView:numberOfRowsInSection:メソッドの実装を変更します

```swift
class TimelineViewController: UITableViewController, UITableViewDelegate, UITableViewDataSource {

    /* 省略 */

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return tweets.count  //  変更
    }

    /* 省略 */
}
```

取得したツイートをセルにセットします。

```swift
class TimelineViewController: UITableViewController, UITableViewDelegate, UITableViewDataSource {

    /* 省略 */

    // 変更
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        
        var cell = tableView.dequeueReusableCellWithIdentifier("tweetCell") as? UITableViewCell
        
        if cell == nil {
            cell = UITableViewCell(style: .Default, reuseIdentifier: "tweetCell")
        }
        
        // ↓ 変更
        let tweet = tweets[indexPath.row] as! Dictionary<String,AnyObject>
        let text = tweet["text"] as! String
        let user = tweet["user"] as! Dictionary<String,AnyObject>
        let name = user["name"] as! String
        let image = UIImage(data: NSData(contentsOfURL: NSURL(string: user["profile_image_url"] as! String)!)!)
        
        let aCell = cell!
        aCell.imageView?.image = image
        aCell.textLabel?.text = text
        aCell.detailTextLabel?.text = name
        
        return aCell
    }

    /* 省略 */
}
```

![comp_02.png](https://qiita-image-store.s3.amazonaws.com/0/35442/e738ecbb-cd08-80a1-eb2b-047a4168562e.png)


##Tips

###UIAlertControllerについて

UIAlertControllerは、アラートはアクションシートを使うために提供されているクラスです。UIKit.Frameworkで提供されています。今回、アカウントの選択画面を実装するのに使用しています。

※ アクションシート
![Screen Shot 2015-08-26 at 5.23.56 AM.png](https://qiita-image-store.s3.amazonaws.com/0/35442/b630850f-e8ea-e69e-bf3d-0456f986e8e0.png)

※ アラート
![images.png](https://qiita-image-store.s3.amazonaws.com/0/35442/aa3f9dd2-5f56-1d71-b9df-6543b9f90ab7.png)



##まとめ

1. フレームワークは特定の機能を使いやすくするプログラムの集まり
2. フレームワークを利用するときはプロジェクト設定の"Build Phase"から追加する
3. ユーザーごとにタイムラインを表示するために、ユーザー情報を使ってタイムラインを初期化する