RealmSwiftとUITableViewを使用してToDoアプリを作成

初めての投稿になります。
私、まだSwiftを勉強し始めて１か月半程の新米であり、初めて投稿をさせていただきます。

現在、Realmを勉強しています。

勉強している際に、Objectにデータを追加する方法や、削除の方法はサンプルが沢山あり、勉強するのは容易だったのですが、
TableViewでのデータの扱いなど、具体的なサンプルがあまり見つからず、結構ハマってしまいました・・・。

自分で勉強したことの復習の意味も込めて、RealmSwift + UITableView でToDoアプリを制作してみました。

ソースコードは[コチラ](https://github.com/TsukasaHasegawa/RealmTableView)から参照・ダウンロードしていただけます。

#Realmの概要
Realmって何？
どうやってインストールするの？
といった疑問は、[公式サイト](https://realm.io/jp/)がすごくわかりやすいので、参照してみてください。

※ちなみに今回は、cocoa podsを使ってプロジェクトにRealmSwiftを追加しました。

#環境
OSX EI Capitan 10.11.6
Xcode 7.3.1
RealmSwift 1.0.2

#まずは画面を作成
<img width="820" alt="スクリーンショット 2016-08-01 21.16.27.png" src="https://qiita-image-store.s3.amazonaws.com/0/135729/9449b4f5-2642-fdfd-765e-90a22dd03552.png">

###TableViewController: UITableViewController

・NavigationControllerをEditor → Embed In → NavigationController の手順で追加

・BarButtonItemを追加し、InputViewControllerにドラッグしsegueを作成（Show, segueのIdentifier → toInputVC）

````TableViewController.swift
import UIKit

class TableViewController: UITableViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int)->Int {
        return 1
    }
    
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexpath: NSIndexPath)->UITableViewCell {
        
        let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
        
        return cell
    }
}
````

###InputViewController: UIViewController
・UILabel
・UITextField → IBOutletをInputViewControllerに追加
・UIButton → IBActionをInputViewControllerに追加

````InputViewController.swift
import UIKit

class InputViewController: UIViewController {

    @IBOutlet weak var titleTextField: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        titleTextField.frame.size.height = 30
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    //Buttonが押された時の処理
    @IBAction func addButtonTapped(sender: AnyObject) {
        
    }
}

````

#Realmを使用するためにクラス作成
Realmでデータを保存するためには、クラスが必要です。
今回は、Realmのクラスを記述するために、新たにファイルを追加しましたが、既存のファイルにclassを記述することも可能です。

````ToDoRealmObject.swift
import RealmSwift

class ToDo: Object {
    dynamic var title = ""
}

````

ToDoのタイトルとなるString型の変数を宣言します。
※通常のSwiftの記述方法と似ていますが、変数の宣言時にdynamicをつけるのを忘れないように注意してください。

#データを保存
データを保存する処理を記述していきます。

````InputViewController.swift
import UIKit
//追加
import RealmSwift

class InputViewController: UIViewController {

````

````InputViewController.swift
@IBAction func addButtonTapped(sender: AnyObject) {
        let newTodo = ToDo()
        
        // textFieldに入力したデータをnewTodoのtitleに代入
        newTodo.title = titleTextField.text!
        
        // 上記で代入したテキストデータを永続化するための処理
        do{
            let realm = try Realm()
            try realm.write({ () -> Void in
                realm.add(newTodo)
                print("ToDo Saved")
            })
        }catch{
            print("Save is Faild")
        }
 // 最後に、TableViewControllerに表示を切り替える       self.navigationController?.popToRootViewControllerAnimated(true)
    }

````

①import UIKitの下に、import RealmSwiftを追記
②InputViewController内の、addButtonTappedを上記のように記述

#データの呼び出し
アプリを一度閉じた後でも、データが引き継がれるように保存することはできましたが、これではまだ不十分です。
保存されたデータを表示するためにデータの取り出しを行う必要があります。

````TableViewController.swift
import UIKit

// 追加
import RealmSwift

class TableViewController: UITableViewController {
````
こちらも同様に、import RealmSwiftを追記します。

````TableViewController.swift
class TableViewController: UITableViewController {
    
    // 追加
    var todoItem: Results<ToDo>!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 追加
        // 永続化されているデータを取りだす
        do{
            let realm = try Realm()
            todoItem = realm.objects(ToDo)
            tableView.reloadData()
        }catch{
            
        }

    }

    // 追加 画面が表示される際などにtableViewのデータを再読み込みする
    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        tableView.reloadData()
    }

````
上記が、保存されたデータを取り出す処理になります。

最後に取り出したデータをTableViewに表示するための処理を書き加えていきます。

````TableViewController.swift
 override func tableView(tableView: UITableView, numberOfRowsInSection section: Int)->Int {
        
        // todoItemの数 = セルの数 
        return todoItem.count
    }
    
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexpath: NSIndexPath)->UITableViewCell {
        let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
        
        // todoItemに代入されたデータをobject:NSArrayに代入
        let object = todoItem[indexpath.row]
        
        //cellのtextLabelのtextにobjectのtitleプロパティを代入 
        cell.textLabel?.text = object.title

        return cell
    }

````
これでデータの取り出しの処理も完了です。

しかし、せっかくここまできたら、データを削除する処理も追加したいですね！

ということで・・・。

````TableViewController.swift
// TableViewのCellの削除を行った際に、Realmに保存したデータを削除する
    override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
        
        
        if(editingStyle == UITableViewCellEditingStyle.Delete) {
            do{
                let realm = try Realm()
                try realm.write {
                    realm.delete(self.todoItem[indexPath.row])
                }
                tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Fade)
            }catch{
            }
            tableView.reloadData()
        }
    }

````
TableViewのセルを左にスワイプし、Deleteボタンを押下すると、TableViewからセルが削除されることはもちろん、Realmに保存されていた該当データも削除されています。

より、ToDoアプリらしくなったのではないでしょうか。

最後にソースコードを全て記載しておきます。

````TableViewController.swift
import UIKit
import RealmSwift

class TableViewController: UITableViewController {
    
    var todoItem: Results<ToDo>!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 永続化されているデータを取りだす
        do{
            let realm = try Realm()
            todoItem = realm.objects(ToDo)
            tableView.reloadData()
        }catch{
            
        }

    }
    
    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        tableView.reloadData()
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int)->Int {
        return todoItem.count
    }
    
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexpath: NSIndexPath)->UITableViewCell {
        let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
        
        let object = todoItem[indexpath.row]
        
        cell.textLabel?.text = object.title
        return cell
    }
    
    // TableViewのCellの削除を行った際に、Realmに保存したデータを削除する
    override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
        
        
        if(editingStyle == UITableViewCellEditingStyle.Delete) {
            do{
                let realm = try Realm()
                try realm.write {
                    realm.delete(self.todoItem[indexPath.row])
                }
                tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Fade)
            }catch{
            }
            tableView.reloadData()
        }
    }
}

````

````InputViewController.swift
import UIKit
import RealmSwift

class InputViewController: UIViewController {
    @IBOutlet weak var titleTextField: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        titleTextField.frame.size.height = 30
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    @IBAction func addButtonTapped(sender: AnyObject) {
        let newTodo = ToDo()
        
        // textFieldに入力したデータをnewTodoのtitleに代入
        newTodo.title = titleTextField.text!
        
        // 上記で代入したテキストデータを永続化
        do{
            let realm = try Realm()
            try realm.write({ () -> Void in
                realm.add(newTodo)
                print("ToDo Saved")
            })
        }catch{
            print("Save is Faild")
        }
        self.navigationController?.popToRootViewControllerAnimated(true)
    }
}

````

````ToDoRealmObject.swift
import RealmSwift

class ToDo: Object {
    dynamic var title = ""
}
````

#完成
<img width="840" alt="スクリーンショット 2016-08-01 22.36.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/135729/16e675f5-889f-0b63-eb32-e7c0a9efc57f.png">

最後までご覧いただき、ありがとうございます。
初めての投稿だったので、至らない点やわかりにくい点が多かったかと思いますが、お付き合いいただき、誠にありがとうございました。

この投稿がどなたかのお役に立てれば、幸いです。

では、失礼いたします。