[Swift]ReactiveKit/BondのObservable2DArrayの例

まだ情報が少なく公式のドキュメントを読んでもわかりずらかったので共有しておく。

```
# 環境
swift 3.0
Xcode version 8.0
SwiftBond/Bond ~> 5.0
```

## Observable2DArrayとは

[Bond](https://github.com/ReactiveKit/Bond )を利用していてUITableViewなどのデータを複数のセクションに分けたい場合に使用するデータモデル。
一つのテーブルにセクションが一つしかない場合は無理して使う必要はなくObservableArrayでも十分。

## 使い方

### データサイド

まずセクションごとのデータを

```swift
let cities = Observable2DArraySection<SectionMetadata, String>(
  metadata: (header: "Cities", footer: "That's it"),
  items: ["Paris", "Berlin"]
)
```

のように定義する。metadataは

```swift
typealias SectionMetadata = (header: String, footer: String)
```

のように定義しておく。headerのタイトルだけしか必要なければStringで

```swift
let cities = Observable2DArraySection<String, String>(
  metadata: "Cities",
  items: ["Paris", "Berlin"]
)
```

でも良い。そしてObservable2DArraySectionの集合が

```swift
let array = MutableObservable2DArray([cities])
```

となる。

データを追加したい場合は

```swift
array.appendItem("Copenhagen", toSection: 0)
```

などのObservable2DArraySectionクラスのメソッドを利用する。

### UIサイド

```swift
struct MyBond: TableViewBond {

  func cellForRow(at indexPath: IndexPath, tableView: UITableView, dataSource: Observable2DArray<SectionMetadata, String>) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
    cell.textLabel?.text = array[indexPath]
    return cell
  }

  func titleForHeader(in section: Int, dataSource: Observable2DArray<SectionMetadata, String>) -> String? {
    return dataSource[section].metadata.header
  }

  func titleForFooter(in section: Int, dataSource: Observable2DArray<SectionMetadata, String>) -> String? {
    return dataSource[section].metadata.footer
  }
}
```

なんとなく見覚えのあるメソッドだが、これをTableViewControllerとは別に(ここではMyBondとして)定義しておく。
この結果TableViewのクラスではviewDidLoadあたりで、

```swift
array.bind(to: tableView, using: MyBond())
```

を呼ぶだけで

```swift
var numberOfSections: Int
func numberOfRows(inSection: Int)
func cellForRow(at indexPath: IndexPath) -> UITableViewCell?
```

などのUITableViewのdelegateを定義しなくても済む。
さらにここでは説明しないがSignalを利用することにより、

```swift
tableView.selectedRow.observeNext { row in
  print("Tapped row at index \(row).")
}.disposeIn(bnd_bag)
```

などと書けるようになる。

## はまったところ

### itemにカスタムモデルを使う場合

上記だと単純に["Paris", "Berlin"]となっているところを

```swift
struct City {
    let name: String
}
```

のようなモデルの配列にしたい場合。

```swif
    let array = MutableObservable2DArray(
        [
            Observable2DArraySection<SectionMetadata, City>(
                metadata: (header: "Cities", footer: "That's it"),
                items: [
                    City(name: "Paris"),
                    City(name: "Berlin"),
                ]
            )
        ]
    )
```

とした上で、MyBondないのObservable2DArray\<SectionMetadata, String\>をObservable2DArray\<SectionMetadata, City\>としてやればよさそうだが、それだとtitleForHeaderやtitleForFooterでエラーになる。
解決方法は

```
class MyBond: TableViewBond {
    typealias DataSource = Observable2DArray<SectionMetadata, City>

    // 略
}
```

とDataSourceという名前に紐付る必要があった。

### MutableObservable2DArrayをクラス間でどうやって共有するか

```swift
struct ViewModel {
    let array = MutableObservable2DArray(
        [
            Observable2DArraySection<SectionMetadata, City>(
                metadata: (header: "Cities", footer: "That's it"),
                items: [
                    City(name: "Paris"),
                    City(name: "Berlin"),
                ]
            )
        ]
    )
}
```

としてUI側からは

```swift
class TableViewController: UITableViewController {

    let viewModel = ViewModel()

    override func viewDidLoad() {
        super.viewDidLoad()

        viewModel.array
            .bind(to: tableView, using: MyBond())
            .disposeIn(bnd_bag)
    }
}

class MyBond: TableViewBond {

    typealias DataSource = Observable2DArray<SectionMetadata, City>

    let viewModel = ViewModel()
    
    func cellForRow(at indexPath: IndexPath, tableView: UITableView, dataSource: DataSource) -> UITableViewCell {
        let city = viewModel.array[indexPath]

        // 略
    }
}
```

とすることでMutableObservable2DArrayの内容をTableに反映させることができた、ただ、動的にデータを追加したりすると途端にクラッシュしてしまう。
気づいてみれば当然だけど、この場合ViewModelをシングルトン化してあげなければいけない。

実装例
https://gist.github.com/ofl/dd005c1568681ff09423900354f075e8

## 使ってみて

Bondを1年近く使っているのだが、ReactiveKitとの統合のためか書き方が何度目かの大掛かりな変更となっている(のでこれから使い始める人は情報が少ないので大変)。
ただObservable2DArraySectionについては以前のMutableArrayを多段にする書き方よりは好ましくなったと思う。