[Swift] Realmを実装して分かったこと色々

#はじめに
Realmを以下の記事を参考に実装しました。
https://qiita.com/pe-ta/items/616e0dbd364179ca284b
以下のリンクから動き確認できます。
https://twitter.com/vex3ex5frd2xcvg/status/1350637210106699777?s=21
そこから色々調べてみたので、自分なりに理解した事を書いてみようと思います。
実装の手順などは、以下の記事から確認してください。
#実装
###① importする
まずは`RealmSwift`をimportします。
`RealmSwift`が必要な箇所でそれぞれimportしてみてください。
はじめはエラーになりますので、1度ビルドする事でimport出来ます。

```ViewController.swift
import UIKit
// RealmSwiftをimportするとエラーになるが、ビルドしたら治る
import RealmSwift
```
###② モデルクラスの作成
ファイルの追加でswiftファイルを作成してください。
このファイルでは、Realmで扱うデータの項目とその型を定義します。
要は、このプロパティに値を保存していくイメージになります。
Realmモデルのプロパティには`@objc`と`dynamic`をつける必要があります。

```TodoModel.swift
import Foundation
import RealmSwift

class TodoModel: Object {
    @objc dynamic var koumoku: String? = nil
}
```
###③ viewControllerに変数を用意
`Results`は「オブジェクトから返される Realm の自動更新コンテナ型」だそうです。
この変数にデータを追加していくことになります。

```ViewController.swift
    // 作成したTodoModel型の変数を用意。
    // Realmから受け取るデータを入れる変数
    var itemList: Results<TodoModel>!
```

###④ viewDidLoadでインスタンス化
今回使用するRealmをインスタンス化して使えるようにします。
次に、`object()`を使用します。これは（）内に指定したデータを全て取得できるものなので、
取得を行い、取得したデータを③で作成した変数に入れます。

```ViewController.swift
override func viewDidLoad() {
        super.viewDidLoad()
        
        // Realmをインスタンス化する
        let realm = try! Realm()
        
        // objects()はRealmに保存されている指定されたデータを全て取得する！
        // object().filter()でさらにフィルタリングして指定することもできるそうです。
        self.itemList = realm.objects(TodoModel.self)
    }
```

###⑤ データを追加して保存していく
今回はbuttonを押したらtextFieldに書いた内容が追加されるようにします。
追加・保存を行いたいAction内で書いてみてください。

1.追加していく場所は、②で作成したモデルクラス内のプロパティになりますので、モデルクラスをインスタンス化していきます。
2.`textField.text`を②で作成した`koumoku`プロパティに入れます。
3.buttonAction内でもRealmを使うので、viewDidLoad内で作成したように、Realmをインスタンス化します。
4.`realm.write`はRealmインスタンスを最新版に更新し、該当する場合は通知を作成すると公式ドキュメントにありました。
２でデータを更新しているので、`realm.write`で最新のものを取得しているわけです。
`realm.add()`は管理されていないオブジェクトを（）内のRealmに追加します。
つまり、`realm.add()`で管理されてない（追加したデータ）をRealmに追加して、
`realm.write`でその最新バージョンを取得しているわけです。

```ViewController.swift
    @IBAction func addButton(_ sender: Any) {
        // 1.モデルクラスをインスタンス化。これでアクセスできるようになる！
        let instanceTodoModel: TodoModel = TodoModel()
        // 2.textFieldの値をぶち込む
        instanceTodoModel.koumoku = self.textField.text
        
        // 3.Realmをインスタンス化してデータベースを取得！viewDidLoadでも行ったやつ
        let realm = try! Realm()
        
        // 4.textFieldの値をデータベースに追加する
        // write は refresh() が呼び出されたかのように Realm インスタンスを最新の Realm バージョンに更新し、該当する場合は通知を生成します。
        try! realm.write {
            realm.add(instanceTodoModel)
        }
        self.tableView.reloadData()
    }
```

###⑥ TableViewの実装
`itemList`にRealmから受け取ったデータが入っているので、それを使用してTableViewCellを作成していきます。


```ViewController.swift
extension ViewController: UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.itemList.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell1", for: indexPath)
        
        let item: TodoModel = self.itemList[indexPath.row]
        
        cell.textLabel?.text = item.koumoku
        
        return cell
    }
```

###⑦ 削除機能

```Viewcontroller.swift
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        // この中でもRealmを使えるようにインスタンス化
        let realm = try! Realm()
        
        if editingStyle == .delete {
            // 削除したいデータを検索する。今回はdeleteしたいcellのindexpath.row番目の項目を削除したいので、それをobjectで指定。
            let deleteItem = realm.objects(TodoModel.self)[indexPath.row]
            
            do{
                // write は refresh() が呼び出されたかのように Realm インスタンスを最新の Realm バージョンに更新し、該当する場合は通知を生成します。
                try realm.write{
                    // 追加の時のaddの削除版
                    realm.delete(deleteItem)
                }
            }catch{
                    print("Error")
                }
            }
        tableView.reloadData()

        }
```

#実際のコード

```ViewController.swift
import UIKit
// RealmSwiftをimportするとエラーになるが、ビルドしたら治る
import RealmSwift

class ViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet weak var textField: UITextField!
    @IBOutlet weak var tableView: UITableView!
    
    // 作成したTodoModel型の変数を用意。
    // Realmから受け取るデータを入れる変数
    var itemList: Results<TodoModel>!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Realmをインスタンス化する
        let realm = try! Realm()
        
        // objects()はRealmに保存されている指定されたデータを全て取得する！
        self.itemList = realm.objects(TodoModel.self)
    }
    
    @IBAction func addButton(_ sender: Any) {
        // モデルクラスをインスタンス化。これでアクセスできるようになる！
        let instanceTodoModel: TodoModel = TodoModel()
        // textFieldの値をぶち込む
        instanceTodoModel.koumoku = self.textField.text
        
        // Realmをインスタンス化してデータベースを取得！viewDidLoadでも行ったやつ
        let realm = try! Realm()
        
        //Realmインスタンスからaddを叩くと、データベースにレコードが追加される
        // textFieldの値をデータベースに追加する
        // write は refresh() が呼び出されたかのように Realm インスタンスを最新の Realm バージョンに更新し、該当する場合は通知を生成します。
        try! realm.write {
            realm.add(instanceTodoModel)
        }
        self.tableView.reloadData()
    }
}

extension ViewController: UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.itemList.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell1", for: indexPath)
        
        let item: TodoModel = self.itemList[indexPath.row]
        
        cell.textLabel?.text = item.koumoku
        
        return cell
    }
    
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        let realm = try! Realm()
        
        if editingStyle == .delete {
            // 削除したいデータを検索する。今回はdeleteしたいcellのindexpathの項目を削除したいので、それをobjectで指定。
            let deleteItem = realm.objects(TodoModel.self)[indexPath.row]
            
            do{
                // write は refresh() が呼び出されたかのように Realm インスタンスを最新の Realm バージョンに更新し、該当する場合は通知を生成します。
                try realm.write{
                    realm.delete(deleteItem)
                }
            }catch{
                    print("Error")
                }
            }
        tableView.reloadData()

        }
}
```

```TodoModel.swift
import Foundation
import RealmSwift

class TodoModel: Object {
    @objc dynamic var koumoku: String? = nil
}

```

#起動してみた
ツイートしてあるので、リンク貼っておきます。
https://twitter.com/vex3ex5frd2xcvg/status/1350637210106699777?s=21
#参考サイト
https://qiita.com/pe-ta/items/616e0dbd364179ca284b
https://naoya-ono.com/swift/realm-update-delete/