Parseを利用したiOSアプリケーションの試作（第二回）


今日は画面を追加して、テキストフィールドやテキストビューに入力したデータをParseに投稿できるようにします。
前回の記事をお読みでない方は、下記記事をご参照ください。

- 前回：[Parseを利用したiOSアプリケーションの試作（第一回）](http://qiita.com/moco3/items/4c726dc1d27301f156dc)

## 完成イメージ
<img class="alignnone size-full wp-image-300" src="http://blog.franliber.co.jp/wp-content/uploads/sites/2/2015/08/parsetest02.gif" alt="parsetest02" width="371" height="676" />

## 画面の作成
まず最初にStoryBoardを使用して、画面から作成していきます。

デフォルトで追加されているViewControllerは一旦削除して、新たにTableViewControllerを追加します。

<img src="https://qiita-image-store.s3.amazonaws.com/0/70446/4b1449bf-4811-7305-623a-04b404e435e9.png" width="640" />

[Editor] - [Embed in] - [Navigation Controller]からNavigation Controllerを設定後、Navigation Controllerを"Is Initial View Controller"に設定します。

<img src="https://qiita-image-store.s3.amazonaws.com/0/70446/6e8e2d50-bf0d-8bf5-d946-4b9eb0d9226f.png" width="640" />

<img src="https://qiita-image-store.s3.amazonaws.com/0/70446/7ec38e58-a704-e6f0-757a-fbb3626da383.png" width="640" />

また、データを登録（参照）する画面としてViewControllerを追加し、TableViewCellからSegueで遷移できるように"control"キーを押しながらドラッグし接続します。

<img src="https://qiita-image-store.s3.amazonaws.com/0/70446/b7c369c1-59f8-f897-9cfa-ad473ed7a475.png" width="640" />

<img src="https://qiita-image-store.s3.amazonaws.com/0/70446/a79c4ebf-39e7-a25a-600c-105374a31725.png" width="640" />

Segueは、Showで接続します。

<img src="https://qiita-image-store.s3.amazonaws.com/0/70446/35a9b70e-50ab-67b8-03bb-2f422a6f2491.png" width="320" />

一通り画面を作ったら、コードを書いていきます。
[File] - [New] - [File]から"Swift File"を選択し、下記クラスの設定をしてください。

### TableViewController
<pre><code>
import UIKit
import Parse
import ParseUI

class TableViewController: PFQueryTableViewController {
    
    override init(style: UITableViewStyle, className: String!) {
        super.init(style: style, className: className)
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        // Configure the PFQueryTableView
        self.parseClassName = "Articles"
        self.pullToRefreshEnabled = true
        self.paginationEnabled = true
        self.objectsPerPage = 30
    }
    
    // Define the query that will provide the data for the table view
    override func queryForTable() -&gt; PFQuery {
        var query = PFQuery(className: "Articles")
        query.orderByAscending("createdAt")
        return query
    }
    
    //override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -&gt; UITableViewCell
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -&gt; PFTableViewCell {

        var cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! PFTableViewCell!
        if cell == nil {
            cell = PFTableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
        }

        // Extract values from the PFObject to display in the table cell
        if let title = object?["title"] as? String {
            cell.textLabel?.text = title
        }
        if let body = object?["body"] as? String {
            cell.detailTextLabel?.text = body
        }
        if let user_id = object?["user_id"] as? String {
            cell.detailTextLabel?.text = user_id
        }
        
        return cell
    }
    
    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

        // Get the new view controller using [segue destinationViewController].
        var detailScene = segue.destinationViewController as! DetailViewController

        // Pass the selected object to the destination view controller.
        if let indexPath = self.tableView.indexPathForSelectedRow() {
            let row = Int(indexPath.row)
            detailScene.currentObject = (objects?[row] as? PFObject)
        }
    }

    override func viewDidAppear(animated: Bool) {

        // Refresh the table to ensure any data changes are displayed
        tableView.reloadData()
    }
}
</code></pre>
設定する部品（コントローラ）を選択してから、Identity Inspectorに表示されいるClassを設定します。

<img src="https://qiita-image-store.s3.amazonaws.com/0/70446/71512ef3-8787-91c6-65c3-4625eebca293.png" width="640" />

### DetailViewController
<pre><code>
import UIKit
import Parse
import ParseUI
import Bolts

class DetailViewController: UIViewController{
    
    @IBOutlet weak var article_title: UITextField!
    @IBOutlet weak var body: UITextView!

    // Container to store the view table selected object
    var currentObject : PFObject?
    

    @IBAction func saveButton(sender: UIBarButtonItem) {
        if let updateObject = currentObject as PFObject? {
            
            // Update the existing parse object
            updateObject["title"] = article_title.text
            updateObject["body"] = body.text
            
            // Save the data back to the server in a background task
            updateObject.saveEventually()
        }else {
            
            // Create a new parse object
            var updateObject = PFObject(className:"Articles")
            
            updateObject["title"] = article_title.text
            updateObject["body"] = body.text
            
            // Save the data back to the server in a background task
            updateObject.saveEventually()
        }
        
        // Return to table view
        self.navigationController?.popViewControllerAnimated(true)
    }
    
    
    override func viewDidLoad() {
        // Unwrap the current object object
        if let object = currentObject {
            article_title.text = object["title"] as! String
            body.text = object["body"] as! String
        }
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}
</code></pre>
TableViewControllerと同様にクラスを作成し、StoryBoardでクラスの設定を行います。

<img src="https://qiita-image-store.s3.amazonaws.com/0/70446/eb42c519-8c51-ac74-6540-2c1757916e3d.png" width="640" />

上記以外に、TableViewで使用するCellの設定が必要で、ClassとStyle、Identifierを以下のように設定します。

<img src="https://qiita-image-store.s3.amazonaws.com/0/70446/c8aecb5a-23b6-68ce-73cc-f564875e8906.png" width="640" />

<img src="https://qiita-image-store.s3.amazonaws.com/0/70446/a30a5259-1b93-79a4-84b1-c45886fea1ed.png" width="640" />

## テストデータの作成
クラス名を変更したらWebブラウザからParseの管理画面を開き、テストデータを登録します。
最初に、SQLでいうところのテーブルにあたるクラスを作成してください。

<img src="https://qiita-image-store.s3.amazonaws.com/0/70446/b2a6c0ce-1b30-8152-d7d2-2f138ab40821.png" width="640" />

今回は、簡易的なブログを想定しているため、"Articles"と命名しました。

<img src="https://qiita-image-store.s3.amazonaws.com/0/70446/28ab4356-ee08-e067-0cf1-ff956ae6d731.png" width="320" />

次に、カラムを追加します。タイトルを表す"title"と本文を表す"body"を追加します。

<img src="https://qiita-image-store.s3.amazonaws.com/0/70446/eea36bfc-b405-2bc3-68c4-1c0b7b9f50a4.png" width="640" />

<img src="https://qiita-image-store.s3.amazonaws.com/0/70446/aefd9ba5-f913-8f7c-ce75-745983e277ca.png" width="320" />

定義ができたら、実際にそれらしいデータをエクセルのように入力しておいてください。

<img src="https://qiita-image-store.s3.amazonaws.com/0/70446/f892ffa1-697f-ab99-070f-7a042201d0e2.png" width="640" />

ここまでできたら、プログラムを実行します。

<img src="https://qiita-image-store.s3.amazonaws.com/0/70446/876f7f6e-392e-9203-e299-0b7d576f92a5.png" width="500" />

上記のようにParseのDBのデータが表示されましたでしょうか？

## 登録（編集）画面の作成
データを登録したり、編集したりできるように、DetailViewControllerにTextFieldとTextViewを追加し、プログラムから操作できるようControlで接続します。

各部品は、画面右下のObject Libraryから追加できます。

<img src="https://qiita-image-store.s3.amazonaws.com/0/70446/73543a5c-07b1-541e-340d-dbaf779dc217.png" width="640" />

<img src="https://qiita-image-store.s3.amazonaws.com/0/70446/9e992963-b759-257b-7fa3-100b15e3b07f.png" width="640" />

ただ追加しただけでは、各デバイスでのレイアウトが崩れてしまうため、制約（Constraints）を追加してやる必要があります。が、ここではその説明は長くなるため割愛させていただきます。

各オブジェクトが配置できたら、コードからプロパティとして扱えるようにConnectします。Connectには大きく2つがあります。

- Outlet：プロパティとして扱う
- Action：タップされた場合など、予め紐付けたメソッドを呼び出す

<img src="https://qiita-image-store.s3.amazonaws.com/0/70446/0043434e-02af-d17e-4f82-f16153f53edc.png" width="640" />

上図のように、アシスタントエディアモードにして、"control"キーを押しながらプロパティを定義するあたりにドラッグしてやります。NavigationBarには、データを保存するようの"Save"ボタンを追加し、空の状態でDetailViewを表示するaddメソッドを実装します。（既に紹介したコードには、addメソッド実装済み）
また、バックグラウンドでデータ保存を行えるようにするために、フレームワークBoltsもインポートしておきます。

## 完成

だいぶ長くなってしまいましたが、これで冒頭で紹介した完成イメージのように動作するはずです。
いかがでしたでしょうか。Parseの使い方、iOSアプリケーションの開発の流れを掴んでいただけたら幸いです。

書ききれていない部分もあるので、詳細はGitのコードを見てみてください。

次回は、ログイン機能を構築してみます。


## 初歩的なエラーにハマった話（余談）
DetailViewControllerの"Title"プロパティ追加時に、下記エラーが発生

<img src="https://qiita-image-store.s3.amazonaws.com/0/70446/29ce6d01-a492-5131-44a9-ff8279a0fa40.png" width="320" />

- Cannot override strong property with weak property
- Property 'title' with type 'UITextField!' cannot override a property with type 'String?'
- Getter for 'title' with Objective-C selector 'title' conflicts with getter for 'title' from superclass 'UIViewController' with the same Objective-C selector
- Setter for 'title' with Objective-C selector 'setTitle:' conflicts with setter for 'title' from superclass 'UIViewController' with the same Objective-C selector

原因：UIViewControllerの"title"プロパティと被っていたためでした。
参考：[iOS Developer Library - UIKit Framework Reference - UIViewController Class Reference](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/)

## GitHub
[https://github.com/kazuyatamakoshi/ParseSample01](https://github.com/kazuyatamakoshi/ParseSample01)

## 参考

- [Use PFQueryTableViewController (from parse) with Swift](http://stackoverflow.com/questions/25069921/use-pfquerytableviewcontroller-from-parse-with-swift)
- [iOS + Swift 1.2 + parse.com - Part 1 (Setting up)](http://blog.bizzi-body.com/2015/02/04/parse-com-getting-started/)