RealmSwiftの辛いところ

ここ一年位RealmSwiftをつかって、感じたこと(ぐち）まとめ
辛い中でマシな方法を模索した知見も書いてあります

## 概要
RealmSwiftを使うと、Swiftの言語で本来できるはずの様々なことができなくなります。辛いです。できないことばかり。
RealmSwiftを使うということは、Swiftではなく、RealmSwiftという環境でコーディングすることだと思ったほうがいい。
ORMapperの都合が多すぎて、全然ドメインロジックに集中させてもらえない。

じゃあ、Realmに更にオレオレWrapperライブラリを被せればいいのかということになるが、

- そんなことしている時点で、Realmを使う必要あるのか？
- RealmのObservingを利用できない

などの疑問がある。

正直、普通にSqliteのORMappingを使うか、ORMappingを自分で書いたほうが良いと思う。
(最近GRDB.swiftというものを使ったが、本当に最高だった。)

私がRealmを使った唯一と言っていいRealmのストロングポイントは、Remoteとのpush型のsyncが便利なこと。ただ、ゆるふわーな要件でのみの話。厳密なノード間のトランザクションは存在しない。エンタープライズ向けとは思えない。(確か2018年中盤のころの知識なので今はなにかあるかもしれない)
(今ならFirestoreの方がイケてると思います！)

以下辛いところ

## スレッドを超えられない
managedObjだとスレッドを超えられない。よって、managedかunmanagedかを常に意識しなくてはいけない。
managed->unmanaged変換も浅い変換しかサポートされていない。

私はHydraというswiftでasync/await的なコードを書けるライブラリを愛用しているが、managedObjをその中で利用しようものならその瞬間クラッシュする。awaitはシングルスレッドじゃなくて、マルチスレッドだけど待ってくれる(コルーチンというらしいです)だけだから。

どんなに辛くても通常のRealmObjectとは別にunmanagedObj用のstructを書いて区別して使うことを強く推奨したい。

## モデル定義

### モデル定義:デフォルト値なしのletを定義
できない。辛い。

## モデル定義:自由にコンストラクタを作れない
[モデル定義:デフォルト値なしのletを定義](モデル定義:デフォルト値なしのletを定義)にも関連するけど、絶対初期値を入れて欲しいときに、

```.swift
class Dog: Object {
  /// 空文字列と未定義の区別できない。開発者が常に脳みそのコストを払って注意すべし。
  @objc dynamic name = ""

  /// init()はnameが未定義に絶対使ってはならないというルールを開発者が常に脳みそのコストを払って徹底すること
  convenience init(name: String) {
    self.init()
    self.name = name
  }
}
```

辛い

### モデル定義:インフラを意識した宣言が強制的に必要になる

```@objc dynamic``` ←邪魔

### モデル定義:自由にOptionalな基本型を定義できない

```RealmOptional``` ←邪魔

### モデル定義:非OptionalなObjectプロパティを定義できない

### ValueObjectを扱えない(Structが扱えない) 
RealmObjectをValueObjectとして利用しようとしたときの話です。
無理に扱おうとすると、きちんと責任を持ってOldObjectを削除しないとゴミだらけになる。というかそういうのを気にしなきゃいけないObjectはValueObjectではない。
やってられません。

なのでどんなに辛くてもRealmがサポートしているプリミティブ型(intとかstringとか）を流用して、決してRealmObjectクラスを作らないようにしなくてはなりません。

```swift:だめな例
class Human: Object {
  @objc dynamic var name: Name
}

// 本来はvalueObjectとして扱いたい
class Name: Object {
  @objc dynamic var first = ""
  @objc dynamic var last = ""
}
```

↓
 
```swift:(私が思う)良い例
class Human: Object {
  @objc dynamic var first = ""
  @objc dynamic var last = ""

  var name: Name { return Name(first: first, last: last) }
}

struct Name {
  let first: String
  let last: String
}
```

### Enumが扱えない


### 基本型のListが扱えない
- なんで```List<Int>```ってやらせてくれないん？

## 継承がサポートされていない
正確に言うと、SuperClassでの検索がサポートされていない
以下の様に、移譲で継承関係を表現しつつ頑張るしか無い。

```.swift
class Animal: Object {
  @objc dynamic isDead = true
  func die() {
    isDead = false
  }
}
class Dog: Object {
  var isDead: Bool { return super!.isDead }
  @objc dynamic super: Animal?
  func die () { super!.die() } // ← 残念なforcedUnwrap
  func しっぽをふる() {
  }
}
```

DownCastするはめになったら大変だ。TemplateMethodを実装しようと思うと大変だ。

## ManagedObjectじゃないとObservingできない
いわゆるCRUDの画面で、RとUの画面の実装を統一しようとしたら詰む。
RealmのObservingはあくまで保存済みObjectのためのものらしい。
Updateの画面だと保存済みRealmObjに監視を効かせながら、textFieldの値を同期していればいい。でもCreateの画面だと、まだRealmに保存していないObjectを扱うことになり、そしてそれは監視できない。

## ManagedObjectはwriteの中で、Unmanagedはwriteの外で変更しなくてはならない
↑ごめんなさい、久々に自分の記事を読み返して加湿修正しているのですが、書いた当時の私が何を言いたいか不明です

## 消しても子Objectが消えない
削除ロジックは自分で管理
ActiveRecordでいうdependency:destroyが恋しいですね。

## RemoteとのTransactionはサポートされていない
RealmのtransactionはあくまでローカルのRealmFileへの書き込みに対してのみ。RealmFile同士のsyncにはTransactionもなにもない。
これでは、厳密な仕様を満たすことができない。書き込みDeviceを一つに絞るとか、書き込み用のサーバーを用意するとかしか思いつきません。
そして、何度も言っているように、そんなことをする羽目になっている時点でRealm使う意味あるのか？という疑問が生じます。

## まとめ
ぐちばかりですが、逆に言うとこれらの辛みを認識して受け入れて、そして利用することで、Realmの様々な恩恵を受けることができます。
すべてを差し出した者にだけ、Realmの神様はホンモノの快適さを与えてくれるのです。

中途半端にフレームワークと喧嘩すると、双方の辛みを受けることになりかねないので、フレームワークとの喧嘩は慎重に…。