[Swift]Parseで簡単 DB連携でTwitter作成


#Parseとはなんぞや
バックエンドをswift(Obj-c)だけで簡単に実装できてしまうもの。

ここではざっくり説明していきます。
詳しくは[Parse iOS Guid](https://parse.com/docs/jp/ios/guide)をみて下さい。

ParseのDBの接続部分だけ見たい方は目次の[ParseDB連携]で飛んで下さい。



# 作成するアプリ

Twitterのようなアプリを作成しながら、Parseのありがたさを見ていきます。
メインはParseで用意されているDB(データベース)に保存するところですね。

![名称未設定.png](https://qiita-image-store.s3.amazonaws.com/0/48036/8d67e521-74ec-dadb-4737-a66439cfd973.png "名称未設定.png")




# Parse.comに新規登録

まずは、[Parse.com](https://parse.com/)に登録します。 

登録完了したら、ホーム画面の右上の『Go to your apps』をクリック。

![Parse_と_Parseを使ってみよう.png](https://qiita-image-store.s3.amazonaws.com/0/48036/89547a42-9ff0-2247-62da-9d11d5f87848.png "Parse_と_Parseを使ってみよう.png")

<br>

次の画面では、Parse上にアプリ新しく作ります。その時に、アプリ名を設定します。

![Home___Parse.png](https://qiita-image-store.s3.amazonaws.com/0/48036/00bac396-3806-4a5a-f069-6e919b9e6f4c.png "Home___Parse.png")

<br>

作成されたアプリをクリックすると...

![Home___Parse.png](https://qiita-image-store.s3.amazonaws.com/0/48036/aad3a307-2cba-b0c9-e7df-56768df74bd7.png "Home___Parse.png")

<br>

アプリの管理ページが登場

![Analytics___Parse.png](https://qiita-image-store.s3.amazonaws.com/0/48036/66c06abd-ce79-93cc-2f02-e8c324726ed5.png "Analytics___Parse.png")


ここのページで保存したデータなどを確認するわけですねえ。

ここまでで、一旦Parse側の操作は終わりです。





#Xcode側で新規プロジェクトを作成

いつものように、Xcodeのプロジェクトを作ります。

# SDKのインポート

作成したプロジェクトにParseに用意されている、SDKをインポートします。

Prase.comに戻り、アプリ管理ページの下部の[Downloads]をクリック


![Analytics___Parse.png](https://qiita-image-store.s3.amazonaws.com/0/48036/a054f258-7dcb-2fab-00dc-d285ae92f142.png "Analytics___Parse.png")

<br>

SDKをクリックすると、SDKをダウンロードすることができます。

![Downloads___Changelogs___Parse.png](https://qiita-image-store.s3.amazonaws.com/0/48036/4c3cd26e-f508-8373-7d6e-f44e7234432d.png "Downloads___Changelogs___Parse.png")

<br>

ダウンロードしたzipファイルを解凍し、その中の以下の２つのファイルをXcodeにドラッグ＆ドロップでインポートしましょう。

- Parse.framework
- Bolts.framework



# その他の必要なフレームワークをインポート

先程の２つのフレームワーク以外に必要なものをインポートします。

以下の画像のように、Linked Frameworks and Libraries まで行き、必要なフレームワークを追加します。

追加するファイル以下のものです。

- AudioToolbox.framework
- CFNetwork.framework
- CoreGraphics.framework
- CoreLocation.framework
- MobileCoreServices.framework
- QuartzCore.framework
- Security.framework
- StoreKit.framework
- SystemConfiguration.framework
- libsqlite3.dylib
- libz.dylib



# Parseで作成したアプリとXcodeのプロジェクトの紐付け

現段階では、Parse.comで作成したアプリと、Xcodeで作成したプロジェクトは独立しているので、
Parse.comで発行される、keyを用いて紐付けます。

## Parse.com

![Edit_Your_App___Parse.png](https://qiita-image-store.s3.amazonaws.com/0/48036/3833412b-49e1-d1bb-4cb4-1e98242f08e1.png "Edit_Your_App___Parse.png")


- Application ID
- Client Key

この２つを使用します。

## Xcodeプロジェクト

AppDelegate.swiftに以下のように編集します。

```AppDelegate.swift
import UIKit
import Parse   //Parseをインポート

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        
        //Parseで取得した[Application ID]と[Client Key]を指定
        Parse.setApplicationId("[Application ID]", clientKey: "[Client Key]")
        
        return true
    }
```

以上の操作で、連携は完了です。


# UIの作成と必要なSwiftファイルの追加

Main.storyboard上はざっくりこんな感じです。



![Main_storyboard_と_PracticeParseLogin_xcodeproj.png](https://qiita-image-store.s3.amazonaws.com/0/48036/002cbd77-9352-1c1f-497c-ed0c7a65d9e6.png "Main_storyboard_と_PracticeParseLogin_xcodeproj.png")




<br>

必要なファイルを作成します。

- Models
	- Tweet.swift(NSObject)
	- TweetManager.swift(NSObject)
- Views
	- TweetTableViewCell.swift(UITableViewCell)
	- TweetTableViewCell.xib   
- ViewControllers
	- TweetsTableViewController.swift(UITableViewController)
	- NewTweetViewController.swfit(UIViewController)

<br>

今回は、Parseで用意さえたDBとの接続がメインなので、それ以外の部分はざざざっと書いていきます。

```Tweet.swft
import UIKit
import Parse

class Tweet: NSObject {
    var name: String!
    var text: String!
    
    init(name: String, text: String) {
        self.name = name
        self.text = text
    }
    
    func saveTweet() {
        //後ほどParseのdbに保存する処理を書く
    }
}
```


```TweetManager.swift
import UIKit
import Parse

class TweetManager: NSObject {
    
    var tweets: Array<Tweet> = []
    static let sharedInstance = TweetManager()
    
    func fetchTweets(callback: () -> Void) {
    	//後ほどParseのdbからツイート情報を取得する処理を記述    
    }
}

```

```TweetTableViewCell.swift
import UIKit

class TweetTableViewCell: UITableViewCell {
    @IBOutlet weak var tweetLabel: UILabel!
    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var tweetImageView: UIImageView!

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
        tweetLabel.numberOfLines = 0
        tweetImageView.clipsToBounds = true
        tweetImageView.contentMode = UIViewContentMode.ScaleAspectFill
    }

    override func setSelected(selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }
    
}

```

```TweetsTableViewController
import UIKit

class TweetsTableViewController: UITableViewController {

    let tweetCollection = TweetManager.sharedInstance
    
    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.registerNib(UINib(nibName: "TweetTableViewCell", bundle: nil), forCellReuseIdentifier: "TweetTableViewCell")
        tableView.estimatedRowHeight = 90
        tableView.rowHeight = UITableViewAutomaticDimension
    }
    
    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        navigationItem.rightBarButtonItem = UIBarButtonItem(title: "New", style: UIBarButtonItemStyle.Plain, target: self, action: "showNewTweetViewController")
        let callback = { () -> Void in
            self.tableView.reloadData()
        }
        tweetCollection.fetchTweets(callback)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }

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

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("TweetTableViewCell", forIndexPath: indexPath) as! TweetTableViewCell
        let tweet = tweetCollection.tweets[indexPath.row]
        cell.tweetImageView.image = UIImage(named: "omoto")
        cell.nameLabel.text = tweet.name
        cell.tweetLabel.text = tweet.text
        return cell
    }

    func showNewTweetViewController() {
        performSegueWithIdentifier("toNewViewController", sender: nil)
    }
    
    override func preferredStatusBarStyle() -> UIStatusBarStyle {
        return UIStatusBarStyle.LightContent
    }
}

```

```NewTweetViewController.swift
import UIKit

class NewTweetViewController: UIViewController, UITextViewDelegate {
    
    @IBOutlet weak var placeholderLabel: UILabel!
    @IBOutlet weak var tweetButton: UIButton!
    @IBOutlet weak var tweetTextView: UITextView!
    @IBOutlet weak var nameTextfield: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()
        tweetTextView.delegate = self
        tweetButton.layer.cornerRadius = 5
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    func textViewShouldBeginEditing(textView: UITextView) -> Bool {
        placeholderLabel.hidden = true
        return true
    }
    
    func textViewShouldEndEditing(textView: UITextView) -> Bool {
        if textView.text.isEmpty {
            placeholderLabel.hidden = false
        }
        return true
    }
    
    @IBAction func tapTweetButton(sender: UIButton) {
        if nameTextfield.text.isEmpty || tweetTextView.text.isEmpty {
            println("Name or text is empty")
        } else {
            let tweet = Tweet(name: nameTextfield.text, text: tweetTextView.text)
            tweet.saveTweet()
            navigationController?.popViewControllerAnimated(true)
        }
    }
}

```

一旦ここまで。

#ParseのDB連携

いよいよ今回のメインです。

##DBにデータを保存
Tweet.swiftのsaveTweet()を編集していきます。

```Tweet.swft
import UIKit
import Parse

class Tweet: NSObject {
    var name: String!
    var text: String!
    
    init(name: String, text: String) {
        self.name = name
        self.text = text
    }
    
    func saveTweet() {
        let tweetsObject = PFObject(className: "tweets")
        tweetsObject["name"] = name
        tweetsObject["text"] = text
        tweetsObject.saveInBackgroundWithBlock { (success, error) -> Void in
            if success {
                println("Tweet has been saved")
            }
        }
    }
}
```

それぞれ解説していきます。

`PFObject(className: "tweets")`では引数に指定した名前のクラスがParse上に存在すればそのオブジェクトを取得し、無ければ生成してくれます。

`tweetsObject["name"] = name`では、作成したテーブルに name という名前のカラムを作成し、値を保存しています。

最後に`saveInBackgroundWithBlock()`メソッドによって、非同期でDBに値を保存します。

## DBから値の取得

続いて、保存した値を取得しましょう。TweetManager.swiftの`fetchTweets()`メソッドを編集します。

```TweetManager.swift
import UIKit
import Parse

class TweetManager: NSObject {
    
    var tweets: Array<Tweet> = []
    static let sharedInstance = TweetManager()
    
    func fetchTweets(callback: () -> Void) {
		let query = PFQuery(className: "tweets")
        query.orderByDescending("createdAt")
        query.findObjectsInBackgroundWithBlock { (tweets, error) -> Void in
            if error == nil {
                self.tweets = []
                for tweet in tweets as! Array<PFObject> {
                    let name = tweet["name"] as! String
                    let text = tweet["text"] as! String
                    let tweet = Tweet(name: name, text: text)
                    self.tweets.append(tweet)
                }
                callback()
            }
        }    
    }
}

```

<br>

`let query = PFQuery(className: "tweets")`
DBから作成したオブジェクトを取得するためには、PFQueryを使用しています。

`query.orderByDescending("createdAt")`
取得した複数のオブジェクトを並び替えています。

`findObjectsInBackgroundWithBlock()`メソッドにてDBから実際にツイートのデータを全て非同期で取得します。
後は取得すたツイートでデータを作成したTweetモデルに変換し、配列に保存しています。



#Parse上で保存したデータの確認


![Banners_and_Alerts_と_tweets___Parse.png](https://qiita-image-store.s3.amazonaws.com/0/48036/5ce44d60-4063-04bb-77b1-550ee05af3be.png "Banners_and_Alerts_と_tweets___Parse.png")

ツイートしたデータを確認することができました。

<br>


#次回予告

次回はParseを使ったログイン機能の実装を紹介しちゃうぞえ