mBaaSを使ってみよう！超簡単にSwiftでTwitterライクなポスト機能を作る(Xcode6 beta5, Parse.com)

![スクリーンショット 2014-08-13 17.35.29.png](https://qiita-image-store.s3.amazonaws.com/0/27810/01460086-e0d3-0d60-8372-579cb4e9ecdf.png) ![スクリーンショット 2014-08-10 17.15.17.png](https://qiita-image-store.s3.amazonaws.com/0/27810/50bcb522-56eb-0cc4-d323-5839c8244973.png "スクリーンショット 2014-08-10 17.15.17.png") 

# 概要
- Swiftと[Parse](https://www.parse.com)を使用して、ユーザー登録と140文字以内のテキストのポストをする
- Twitterのように、他の人のポスト内容がタイムラインとして表示される
- Parseを使うので、ローカルDBは不要で、超簡単にデータベースを作成でき、カラム追加やデータ作成ができる

# Github
- [kiiita/SwiftAndParse](https://github.com/kiiita/SwiftAndParseApp)

# Other Contents about Swift
- [SwiftのNotificationでHello, Worldして、アクションを3つ作ってみた(Xcode6 beta4)](http://qiita.com/kiiita/items/453426e3873e58dc8768)
- [SwiftでTo Doリストを作ってみた（Xcode6 beta4, Tabbed Application, UITableView, UITextField）](http://qiita.com/kiiita/items/22bcb7c503eaa2f2d423)

# 手順
## 1. Parse.com側の設定

![スクリーンショット 2014-08-10 16.43.25.png](https://qiita-image-store.s3.amazonaws.com/0/27810/56061729-e92d-f0b5-2aa9-2393e348740b.png "スクリーンショット 2014-08-10 16.43.25.png")

- 30req/sまでは無料で使える！

![スクリーンショット 2014-08-10 16.44.18.png](https://qiita-image-store.s3.amazonaws.com/0/27810/2b20a452-474e-6394-1697-0a6fbee2b7fd.png "スクリーンショット 2014-08-10 16.44.18.png")


- まず、ユーザー登録などをしておく
- 続いて、[Parse.com](https://www.parse.com)でCreate New Appをする

![スクリーンショット 2014-08-10 16.46.12.png](https://qiita-image-store.s3.amazonaws.com/0/27810/80fcdaf6-6c47-ab61-7506-74dd5f443b33.png "スクリーンショット 2014-08-10 16.46.12.png")

- 色々出てくるが、無視して”QuickStartGuide"を押す

![スクリーンショット 2014-08-10 16.46.25.png](https://qiita-image-store.s3.amazonaws.com/0/27810/1a4c88c1-73e4-7aee-f3d1-b0a0bc6bacec.png "スクリーンショット 2014-08-10 16.46.25.png")


- data -> mobile -> iOS -> ObjectiveC -> Existing Projectと進む
- 進んでいった先のページに書いてある通りに準備を行う
 - SDKのインストール
 - Parse.comを利用するために必要なFrameworksを入れる
  - 必要なFrameworkは合計10個なので、抜け漏れがないように注意。

## 2. XcodeでParseと接続テスト
- Xcodeの方で、New File(cmd+N)して、Obj-Cでファイルを作成。ファイル名は何でもOK。ここではhogeとした。
 - hoge.mとProjectName-Bridging-Header.hという２つのファイルが生成される。
 - hoge.mというファイルは不要なので、削除する。また、ProjectName-Bridging-Header.hに下記コードを追加する。
 
```ProjetName-Bridging-Header.swift
#import <Parse/Parse.h>
```
- 続いて、Parse.comにObj-Cで書いてある初期設定をSwiftに書き換えてみて、下記のようなテストオブジェクトを生成する。（下記はSwiftに書き換え済み）

```AppDelegate.swift 
// Parse.comの設定
Parse.setApplicationId("ClientID", clientKey: "ClientKEY")

var testObject:PFObject = PFObject(className: "TestObject")
testObject["foo"] = "bar"
testObject.setObject("user1", forKey: "user")

// 上記コードの説明は以下。
// var 適当な変数:PFObject = PFObject(className: "テーブル名")
// testObject["カラム名"] = "テスト用に突っ込むデータ"
// testObject.setObject("テスト用に突っ込むデータ", forKey: "カラム名")
        
testObject.saveInBackground()
        
/* こちらがParseのIntroductionに書いてあるObj-cの元のコード。一応張っておきます。
[Parse setApplicationId:@"ClientID"
clientKey:@"ClientKEY"];
        
PFObject *testObject = [PFObject objectWithClassName:@"TestObject"];
testObject[@"foo"] = @"bar";
[testObject saveInBackground];
*/
``` 

- これだけでParseとの接続が完了☆
- このあとParse.comにあるtestボタンを押してみて”Congrats!"が表示されれば接続完了！
- テストが完了したら、上記のコードは削除してOK

## 3. Tweet機能の実装
### 3-1. StoryboardにTableViewControllerを追加。NavigationControllerも追加して、それをTableViewControllerに追加する
 - Label２個（ユーザー名とポスト時間を表示するためのもの）とTextView（140文字のテキストを入力するためのフォーム）を追加して、Bar Button（新しく投稿するためのもの）をTableViewControllerに追加。
- ViewController（新しいポストをする画面のためのもの）を生成して、TableViewControllerのBar buttonとpushの関係でひもづける

### 3-2. StoryboardとControllerをひもづける
 - cmd+NでNew Fileして、Cocoa Touch Classを選択
 - TimelineTableViewControllerとComposeViewControllerを作成
 - それぞれのControllerをStoryBoardにひもづける。
 - さらにCocoa Touch ClassでTweetTableViewCellを作成し、Timeline Table ViewのCellにひもづける。（ひもづける時は、左下のレイヤーを表示するボタンを押してやる）
 - 右カラムのインスペクターからTable View Cellのidentifierを”Cell”に変更

### 3-3. ユーザー作成のためのAlertポップアップを作成する

```TimelineTableViewController.swift
// アプリを起動した時にログインアラートを表示する。viewDidLoadではないので注意。
 override func viewDidAppear(animated: Bool) {
        if (!PFUser.currentUser()) {
            var loginAlert:UIAlertController = UIAlertController(title: "Sign UP / Loign", message: "Plase sign up or login", preferredStyle: UIAlertControllerStyle.Alert)
            
			 // ユーザーネームとパスワードの入力
            loginAlert.addTextFieldWithConfigurationHandler({
                textfield in
                textfield.placeholder = "Your username"
            })
            
            loginAlert.addTextFieldWithConfigurationHandler({
                textfield in
                textfield.placeholder = "Your Password"
                textfield.secureTextEntry = true
            })
            
            loginAlert.addAction(UIAlertAction(title: "Login", style: UIAlertActionStyle.Default, handler: {
                    alertAction in
                let textFields:NSArray = loginAlert.textFields as NSArray
                let usernameTextfield:UITextField = textFields.objectAtIndex(0) as UITextField
                let passwordTextfield:UITextField = textFields.objectAtIndex(1) as UITextField

                // ここではtweeterをユーザーの変数として作成
                var tweeter:PFUser = PFUser()
                // UITextFieldに入力された内容を代入する
                tweeter.username = usernameTextfield.text
                tweeter.password = passwordTextfield.text
                
                // Parseに送信
                tweeter.signUpInBackgroundWithBlock{
                    (success:Bool!, error:NSError!)->Void in
                    if !error{
                        println("Sign up successful")
                    }else{
                        let errorString = error.userInfo["error"] as NSString
                        println(errorString)
                    }
                }
                
                }))
            
            self.presentViewController(loginAlert, animated: true, completion: nil)
        }
    }
```

- ここでiOS Simulatorを立ち上げてみて、ログインをしてみる
- そのあと、Parse.comに行ってDashboardをリロードすると今作成したユーザーが出てくる（お手軽！）

![スクリーンショット 2014-08-10 16.39.31.png](https://qiita-image-store.s3.amazonaws.com/0/27810/5f19adb5-e2ff-517d-5a8c-59749d49f865.png "スクリーンショット 2014-08-10 16.39.31.png")

### 3-4. Tweetsテーブルの作成
```ComposeViewController.swift
@IBAction func sendTweet(sender: AnyObject) {
        // Tweetする内容を保存するclass（テーブル）の作成
        var tweet:PFObject = PFObject(className: "Tweets")
        
        // カラムを作成する。ここでは、ユーザーとTweet内容用のカラムを作成。
        tweet["content"] = tweetTextView.text
        tweet["tweeter"] = PFUser.currentUser()
        
        // Parseに送信
        tweet.saveInBackground()
        
        // Tweet一覧が表示されるTimelineTableViewControllerに戻る
        self.navigationController.popToRootViewControllerAnimated(true)
        
    }
```

- ComposeViewControllerがParse.comとのTweetの連携をしたので、Tweetしてみて、Parse.comのDashboardをリロードすると新たなテーブルが出てきて、反映されていることが確認できる

![スクリーンショット 2014-08-10 16.39.18.png](https://qiita-image-store.s3.amazonaws.com/0/27810/275ae4d5-27b2-d760-fc9b-868313c939f0.png "スクリーンショット 2014-08-10 16.39.18.png")


## 4. 投稿したTweet内容を取得して、Viewに表示する
```TimelineTableViewController.swift
var timelineData:NSMutableArray = NSMutableArray()
    
    // beta5からinitの前方にrequiredが必要になった
    required init(coder aDecoder: NSCoder!) {
        super.init(coder: aDecoder)
    }
    
    // Parseからデータの取得
    func loadData(){
        timelineData.removeAllObjects()
        
        // Tweetsテーブルを呼び出す
        var findTimelineData:PFQuery = PFQuery(className: "Tweets")
        
        findTimelineData.findObjectsInBackgroundWithBlock{
            (objects:[AnyObject]!, error:NSError!)->Void in
            
            if !error{
                for object in objects{
                    self.timelineData.addObject(object)
                }
                
                let array:NSArray = self.timelineData.reverseObjectEnumerator().allObjects
                self.timelineData = array as NSMutableArray
                
                self.tableView.reloadData()
            }
        }
    }

```
- ログイン、サインアップのアラートのコードの上部に下記を追加（viewDidAppearの所）

```TimelineTableViewController.swift
// tweetのデータを取得
self.loadData()
```

- UITableViewに関連するコードも追加する

```TimelineTableViewController.swift
 override func numberOfSectionsInTableView(tableView: UITableView!) -> Int {
        // #warning Potentially incomplete method implementation.
        // Return the number of sections.
        return 1
    }

    override func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete method implementation.
        // Return the number of rows in the section.
        // Tweetsデータの数だけrowを返す
        return timelineData.count
    }
```

- Tweetの内容とユーザーをParseから取得する

```TimelineTableViewController.swift
 override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
        let cell:TweetTableViewCell = tableView!.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as TweetTableViewCell
        
        let tweet:PFObject = self.timelineData.objectAtIndex(indexPath!.row) as PFObject
        
        // デザイン部分なので無視してOK
        cell.tweetTextView.alpha = 0
        cell.timestampLabel.alpha = 0
        cell.usernameLabel.alpha = 0
        
        // Tweetの内容をParse.comから取得
        cell.tweetTextView.text = tweet.objectForKey("content") as String
        
        var dataFormatter:NSDateFormatter = NSDateFormatter()
        dataFormatter.dateFormat = "yyyy-MM-dd HH:mm"
        cell.timestampLabel.text = dataFormatter.stringFromDate(tweet.createdAt)
        
        // objectIdをforeignKeyとして、user(tweeter)を取得
        var findTweeter:PFQuery = PFUser.query()
        findTweeter.whereKey("objectId", equalTo: tweet.objectForKey("tweeter").objectId)
        
        findTweeter.findObjectsInBackgroundWithBlock{
            (objects:[AnyObject]!, error:NSError!)->Void in
            if !error{
                let user:PFUser = (objects as NSArray).lastObject as PFUser
                cell.usernameLabel.text = "@\(user.username)"
                
                
                // CELLの表示にDurationをつけて、ほわっと表示する
                UIView.animateWithDuration(0.5, animations: {
                    cell.tweetTextView.alpha = 1
                    cell.timestampLabel.alpha = 1
                    cell.usernameLabel.alpha = 1
                    })
            }
        }

        // Configure the cell...

        return cell
    }

```

- 入力しているときの残り文字数のカウント

```ComposeViewContorller.swift
    func textView(textView: UITextView!,
        shouldChangeTextInRange range: NSRange,
        replacementText text: String!) -> Bool{
            
            // 入力文字数のカウント
            var newLength:Int = (textView.text as NSString).length + (text as NSString).length - range.length
            var remainingChar: Int = 140 - newLength
            
            charRemainingLabel.text = "残り\(remainingChar)"
            
            return (newLength > 140) ? false : true
            
            
    }
```

---

★女性エンジニア向けのイベントやります
<a class="doorkeeper-registration-widget" href="http://zigexn.doorkeeper.jp/events/14687">9/17(水)開催 - ZIGExN Swift Girls 〜集え☆iPhoneアプリを作りたい女子〜</a><script src="http://widgets.doorkeeper.jp/w/widget.js"></script>