【Swift5】Realmでデータを管理する

# はじめに
　メモアプリのタイトルのみをUserDefaultsで管理していたのですが、タグや日付などの情報も管理したくなり、UserDefaultsで保持すると複雑になりそうだったので、Realmへの移行を試みました！
　データの挿入まではわりとスムーズにできたのですが、autoIncrementや更新系の設定で、苦戦したので、その辺りを中心にまとめます！

# 環境
- xcode 11.3.1
- swift 5.1.3

# Realmにデータを挿入する
　Realmにデータを挿入するには、Objectのモデルを作り、`add`という関数を使って保存してあげれば、データが挿入されるようです。

```swift:Memo.swift
import Foundation
import RealmSwift

class Memo: Object {
    @objc dynamic var title : String = ""
    @objc dynamic var content : String = ""
    @objc dynamic var tags : String = ""
    @objc dynamic var createdDate : Date = NSDate() as Date
    @objc dynamic var updatedDate : Date = NSDate() as Date
    
}
```
モデルは↑のように作りました！
データを挿入する際のコードはこんな感じです！

```swift:MemoTableViewController.swift(一部抜粋)
let memoModel = Memo()
let realm = try! Realm()

// オブジェクトに値をセットする
memoModel.title = memo
memoModel.content = memo
memoModel.tags = "test1"

// DBに書き込む
try! realm.write {
    realm.add(memoModel)
}
```
データを挿入するところまではこちらの記事を参考にしていただければ、わかりやすいと思います！
私もこちらを参考に実装しました！
[[Realm][Swift4対応 完全保存版] 3.モデルオブジェクトの作成と書き込み](http://swift.hiros-dot.net/?p=590)

# Realm内のデータを確認する
Realmのデータを確認する手順は次のようになります。

1. Realm Browserをインストールする
1. アプリを起動し、RealmのデータベースのPathを調べる
1. ターミナルから`open`コマンドで2のPathを開く

私はこの手順で動作確認できました！
![スクリーンショット 2020-02-25 0.24.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/583780/b2238b75-39fe-cd4a-4966-6c397f0693b4.png)

動作確認する方法はこちらの記事が参考になると思います！
[【Swift】Realm BrowserでRealm Mobile Databaseの中身を確認する](https://qiita.com/ryoegu/items/3a7543a954673396d10d)

# idを自動連番(Auto Increment)で入れる
RealmはデフォルトでAuto Incrementするような機能がなさそう？だったので私はRObjectというクラスを作り、
そこにAuto Incrementを実装して、モデルで継承するように実装しました。

```swift:Memo.Swift
import Foundation
import RealmSwift

class Memo: RObject { // RObject←Objectに変更
    @objc dynamic var title : String = ""
    @objc dynamic var content : String = ""
    @objc dynamic var tags : String = ""
    @objc dynamic var createdDate : Date = NSDate() as Date
    @objc dynamic var updatedDate : Date = NSDate() as Date
    
}
```
```swift:RObject.swift
import Foundation
import Realm
import RealmSwift

class RObject: Object {
    // ID
    @objc dynamic var id = 0

    // データを保存。
    func save() {
        let realm = try! Realm()
        if realm.isInWriteTransaction {
            if self.id == 0 { self.id = self.createNewId() }
            realm.add(self, update: true)
        } else {
            try! realm.write {
                if self.id == 0 { self.id = self.createNewId() }
                realm.add(self, update: true)
            }
        }
    }

    // 新しいIDを採番します。
    private func createNewId() -> Int {
        let realm = try! Realm()
        return (realm.objects(type(of: self).self).sorted(byKeyPath: "id", ascending: false).first?.id ?? 0) + 1
    }

    // プライマリーキーの設定
    override static func primaryKey() -> String? {
        return "id"
    }
}
```
```swift:MemoTableViewController.swift(一部抜粋)
        if let selectedIndexPath = self.tableView.indexPathForSelectedRow{
            self.memos[selectedIndexPath.row] = memo
            
            // realm
            let realm = try! Realm()
            let memoModel = realm.objects(Memo.self).filter("id == " + String(sourceVC.memoId!)).first ?? Memo()
            try! realm.write {
                memoModel.title = memo
                memoModel.save() // realm.addから↑実装した.saveに変更
            }
        }else{
            self.memos.append(memo)
            // realm
            let memoModel = Memo()
            let realm = try! Realm()
            try! realm.write {
                memoModel.title = memo
                memoModel.content = memo
                memoModel.tags = "test1"
                memoModel.save()
            }

```


## 参考にした記事
- https://gist.github.com/syou007/801fa5ddbd11d1d57ac0255938f65b41
- https://qiita.com/syou007/items/267612c402fdd28de7a8



# データの更新をする
　ここまではわりとスムーズに行ったのですが、データ更新あたりから妻付き始めました。。
`Realm().add(_:update:)`を使ってデータを更新するという情報が多かったのですが、なぜかエラーに。。
色々調べた結果、次のように実装変えたらコンパイル通ったので参考にしてください。
おそらく、RealmSwiftの最新版だと
[Realmのドキュメント](https://realm.io/jp/docs/swift/latest/#%E3%83%97%E3%83%A9%E3%82%A4%E3%83%9E%E3%83%AA%E3%82%AD%E3%83%BC%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E4%BD%9C%E6%88%90%E6%9B%B4%E6%96%B0%E3%81%99%E3%82%8B)

```
realm.add(self, update: true)
// ↓
realm.add(self, update: .modified)
```


# 元のテーブル(モデル)を削除する
　実装自体のエラーはなくなって自動連番の機能や主キーのIDが入れられるようになりました。
が、今度は起動時にIDがテーブルに存在しないというエラーが出ました。。
　調べたところ、テーブルの形式を変更したので、元々入っていたデータにIDがなく、エラーのようでした。。
対処方法は次の2つでした。

- 既存のテーブル(Object)の削除
- マイグレーション

まだ、不要なデータしかなかったのとマイグレーションが最初うまくできなかったので今回は、一旦既存テーブル削除を実施！

```swift:AppDelegate.swift
// 既存テーブルの削除
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        let realmURL = Realm.Configuration.defaultConfiguration.fileURL!
        let realmURLs = [
            realmURL,
            realmURL.appendingPathExtension("lock"),
            realmURL.appendingPathExtension("note"),
            realmURL.appendingPathExtension("management")
        ]
        for URL in realmURLs {
            do {
                try FileManager.default.removeItem(at: URL)
            } catch {
                // handle error
            }
        }
  return true
}
```
　このコードを実装した状態で、アプリケーション起動するとテーブルが削除されるので、新しいモデルが挿入できるようになると思います！
　マイグレーションの方法に関しては、勉強して追記しようと思います😓
## 参考にした記事
- https://swift.hiros-dot.net/?p=645#toc22

# まとめ
　今回はRealmでデータ管理がするのに必要な実装をしてみました。`update`のあたりとかは特にBool型を引数に実装をしている記事が多かったので、要注意だと思います！
　また、swift周りは技術の進化が早く、どんどんバージョンが入れ替わり、参考にした記事のコードが動かないということがザラにあったので、書かれた時期と環境のバージョンが大事だなと思いました！
あと余談ですがswiftの独特のコーディングにも少し慣れてきた気がしました(segueなど)！笑