[iOS]RealmFlowというライブラリを作ってみた[Realm]


## はじめに

[Realmの処理を再利用可能かつ合成可能にする@社内iOS勉強会](https://www.slideshare.net/yukitakahashi3139241/realmios)というスライドを見て、処理を記述しておいて、トランザクションは最後に走らせるという発想がすごいなぁと思った。

のだが、処理をflatMapで合成していくのが個人的にかったるかったので、考え方だけぱk…お借りして自分が書きやすいようにかけるライブラリを作ってみた。

## こういう風に書きたかったコード例

まずは、どういう風に書きたかったのかというと、

```swift
import RealmSwift
import RealmFlow

class Dog : Object {
    @objc dynamic var name: String = ""
}

class Cat : Object {
    @objc dynamic var name: String = ""
}
```

のようにモデルを定義しておいたら、

```swift
        // データの準備
        let pochi = Dog()
        pochi.name = "Poti"
        let tama = Cat()
        tama.name = "Tama"
        let hachi = Dog()
        hachi.name = "Hachi"
        let taro = Dog()
        taro.name = "Taro"
        let jiro = Dog()
        jiro.name = "Jiro"
        
        // 処理の流れ(Flow)をだらだらと書いていく。
        let flow = Realm.Flow.deleteAll() // 最初に今までのデータを全削除（必須ではない）
            .add(pochi)  // ポチ追加
            .add(tama)
            .add(hachi)
            .objects(Dog.self)  // クエリ（犬）
            .subscribe { results in  // クエリ結果を処理
                print("first subscribe")
                for dog in results {
                    print("dog.name: \(dog.name)")
                }
            }
            .objects(Cat.self)  // クエリ（猫）
            .add(taro) // 突然、太郎を追加してみたり
            .subscribe { results in  // クエリ（猫）の結果を処理
                print("second subscribe")
                for cat in results {
                    print("cat.name: \(cat.name)")
                }
            }
            .delete(pochi) // ポチ削除
            .add(jiro)
            .objects(Dog.self) // クエリ（犬）２度目
            .subscribe { results in
                print("third subscribe")
                for dog in results {
                    print("dog.name: \(dog.name)")
                }
            }
        
        // 処理を書いたら、実行する。
        let realm = try! Realm()
        let _ = try? realm.run(flow: flow) // ここではじめてトランザクション開始
```

のように、だらだらと操作を続けて書きたいわけですよ。

## 使い方

で、そういう風に書けるように出来たのが[こちら](https://github.com/JunSuzukiJapan/RealmFlow)。

~~使い方は、今のところ、Carthageを使うしかないです。
（CocoaPodsには、後で対応予定です。）~~
(CocoaPods対応しました。）

### Carthage

Cartfileに

```Cartfile
github "JunSuzukiJapan/RealmFlow"
```

と記述して、


```shell:
carthage update
```

としてインストールしてください。

その後の設定は、Carthageのいつもの使い方でお願いします。
よくわからない方は、

[Carthageを使ってビルド時間を短縮しよう](https://qiita.com/yutat93/items/97fe9bc2bf2e97da7ec1)
[【Swift3】RealmSwiftをCarthageで導入する](https://qiita.com/nakabonne/items/153b4c737fe273186b6e)

あたりを参照してください。

### CocoaPods

Podfileに ``` pod 'RealmFlow', '~> 0.1'```と記入して ```pod install``` でインストールしてください。


## ドキュメント

ドキュメントはいずれGitHubの方に書いていく予定です。

なにか問題とかありましたら、GitHubのissueに ***日本語*** でお願いします。


## コード例

これだけだと不親切すぎるので、コード例をいくつか。


```swift:例２　データ追加
        let poti = Dog()
        poti.name = "Poti"
        let tama = Cat()
        tama.name = "Tama"
        let hachi = Dog()
        hachi.name = "Hachi"
        
        var flow = Realm.Flow.add(tama)
            .add(poti)
        let flow2 = Realm.Flow.add(hachi)
        flow = flow.combine(flow2)  // 処理と処理を合体
        
        let realm = try! Realm()
        try? realm.run(flow: flow)
```

```swift:例３　クエリ
        let flow = Realm.Flow.objects(Dog.self)
            .subscribe { results in
                print("results = \(results)")
                for dog in results {
                    print("1 name: \(dog.name)")
                }
            }
            .filter({ $0.name != "Poti" })
            .subscribe { results in
                for dog in results {
                    print("2 name: \(dog.name)")
                }
        }
        
        let realm = try! Realm()
        let _ = try? realm.run(flow: flow)
```


## CRUD Example

### Create

```swift:Create
    let hachi = Dog()
    hachi.name = "Hachi"
    
    let flow = Realm.Flow.add(hachi)
    
    let realm = try! Realm()
    let _ = try? realm.run(flow: flow)
```

### Read

```swift:Query
    let flow = Realm.Flow.objects(Dog.self)
        .subscribe { results in
            let name = results.first?.name ?? "No dog"
            print("dog name: \(name)")
        }

    let realm = try! Realm()
    let _ = try? realm.run(flow: flow)
```

### Update

データの更新は ```realm.beginWriteTransaction()``` を呼び出していないと出来ないので、 ```subscribe_with_write_permission``` を使って確実に writeTransaction 内で行う必要がある。

```swift:Update
    let flow = Realm.Flow.objects(Dog.self)
        .subscribe_with_write_permission { (realm, results) in
            results.first?.name = "Pochi"
        }

    let realm = try! Realm()
    let _ = try? realm.run(flow: flow)
```

### Delete

データの削除も更新と同様に ```subscribe_with_write_permission``` を使って確実に writeTransaction 内で行う。


```swift:Delete
    let flow = Realm.Flow.objects(Dog.self)
        .subscribe_with_write_permission { (realm, results) in
            let dog = results.first
            if dog != nil {
                realm.delete(dog!)
            }
        }
    
    let realm = try! Realm()
    let _ = try? realm.run(flow: flow)
```