TableViewでチャット風な何か(RxSwift)

RxSwiftをやろうかなと思い、チャット風なUIをとりあえず作って見たのでメモがてら投稿

# 概要
- サーバー等置いてないので、入力した文字を表示していくだけ
- 送信ボタンを押したら、text入力欄に入力されてるtextをリストに追加して表示

これだけしかやってません:cry:

# Rx風な考え方
なにかActionがあったときに、なにかするという予約を入れておくようなイメージで作りました

# クラス説明
## 要素クラス
```swift
struct VoiceItem {
	var user: String
	var message: String
}
```

送る`user`と送る`message`のみのクラスです
特に難しいこともしてないです

## Cellクラス
```swift
class VoiceCell: UITableViewCell {
	
	@IBOutlet weak var userLabel: UILabel!
	@IBOutlet weak var messageLabel: UILabel!
	
	func item(_ newValue: VoiceItem) {
		userLabel.text = newValue.user
		messageLabel.text = newValue.message		
	}
}
```

`user`と`message`を表示する`UILabel`を設置しておきます
先ほどの要素クラスを受け取ったら、各ラベルに情報を反映させます

## 要素管理クラス
```swift
class VoiceModel {
	let itemsEvent = Variable<[VoiceItem]>([])
	var items: Observable<[VoiceItem]> {
		return itemsEvent.asObservable()
	}
	
	var rx_items: AnyObserver<String> {
		return AnyObserver<String>(eventHandler: { (e) in
			self.addItem(item: VoiceItem(user: "A", message: e.element!))
			self.addItem(item: VoiceItem(user: "B", message: e.element! + "ですか！すごーい！"))
		})
	}
	
	init() {
	}
	
	func addItem(item: VoiceItem) {
		itemsEvent.value.append(item)
	}
}
```
ここで`RxSwift`がやっと登場します
`items`が要素の配列になってます。監視される対象なので`Observable`で定義します
`addItem`は要素を追加するメソッドです
`rx_items`は、なにかあったときにする処理を書いておきます。これにより監視される対象からこの処理を呼び出してもらうことができます。（監視される対象と`rx_items`のつなぎは`ViewController`クラスで説明します）

## ViewControllerクラス
```swift
class ViewController: UIViewController {

	@IBOutlet weak var tableView: UITableView!
	@IBOutlet weak var textField: UITextField!
	@IBOutlet weak var send: UIButton!

	let disposeBag = DisposeBag()
	let model = VoiceModel()	
	
	override func viewDidLoad() {
		super.viewDidLoad()
		// Do any additional setup after loading the view, typically from a nib.

		// TableViewと要素の配列
		model.items.bindTo(self.tableView.rx.items(cellIdentifier: "Cell", cellType: VoiceCell.self)) { (row, element, cell) in
			cell.item(element)
		}.disposed(by: disposeBag)

		// 送信ボタンを押す
		self.send.rx.tap.map{ self.textField.text ?? "" }.bindTo(model.rx_items).disposed(by: disposeBag)
		self.send.rx.tap.subscribe(onNext: { _ in
			self.textField.text = nil
		}).disposed(by: disposeBag)

		// text入力された時とボタンの有効
		self.textField.rx.text.map { ($0?.characters.count)! > 0 }.bindTo(self.send.rx.isEnabled).disposed(by: disposeBag)
		
	}

	override func didReceiveMemoryWarning() {
		super.didReceiveMemoryWarning()
		// Dispose of any resources that can be recreated.
	}

}
```

ここがわかりにくいと思うので、少し詳しく考え方を...

### TableViewと要素の配列
分解して説明していきます

```swift
model.items.bindTo()
```
まず、要素の配列`items`に変化があったときに`bindTo()`の引数に処理をするように予約しておきます
変化があったときとは具体的には、`items`に要素が追加されたときなどです

```swift
self.tableView.rx.items(cellIdentifier: "Cell", cellType: VoiceCell.self)) { (row, element, cell) in
			cell.item(element)
		}
```
`self.tableView.rx.items`にはすでに処理が書かれているのでそれを使います
見てわかるかと思いますが、`Cell`を作る処理（`tablleView`に追加もしてくれます）です
`cellIdentifier`、`cellType`は普段使ってるかと思います
Closureの引数は、

- `row`がIndex
- `element`が要素の配列のIndex番目の要素
- `cell`が`cellType`で指定した型の`Cell`

です
今回は、`(row: Int, element: VoiceItem, cell: VoiceCell)`という型になります

```swift
.disposed(by: disposeBag)
```
最後に、`disposed`ですが、これは処理の予約の破棄をしてくれます
今回は、`DisposeBag()`を引数に渡していますので、このクラス(`ViewController`)が破棄されるときに破棄してくれます

### 送信ボタンを押す
```swift
self.send.rx.tap.map{ self.textField.text ?? "" }.bindTo(model.rx_items).disposed(by: disposeBag)
```
送信ボタンを押したときは、`self.send.rx.tap`で監視できます
`tap`したときにtextがあるかを`map`内で判断してます（`filter`でもよいかも...）
先ほど作った、`rx_items`をbindすることによって、`rx_items`に書いた処理をさせることができます

```swift
self.send.rx.tap.subscribe(onNext: { _ in
			self.textField.text = nil
	}).disposed(by: disposeBag)
```
こちらは、`tap`があったときに、入力していたtextを削除します

### text入力された時とボタンの有効
```swift
self.textField.rx.text.map { ($0?.characters.count)! > 0 }.bindTo(self.send.rx.isEnabled).disposed(by: disposeBag)
```

`self.textField.rx.text`でtextの入力を監視することができます
textが1文字以上入力された時、`map`で`bool`の値にして、`self.send.rx.isEnabled`に入れるという形になります
これで、textが入力されたら、送信ボタンが押せるようになる処理の予約ができました

![スクリーンショット 2017-05-08 16.20.38.png](https://qiita-image-store.s3.amazonaws.com/0/150615/7bfa2175-03cd-0e7f-c58a-06a80225e1c4.png)
![スクリーンショット 2017-05-08 16.20.47.png](https://qiita-image-store.s3.amazonaws.com/0/150615/26cfd629-cd37-ce05-7dcd-f95e0577f6d9.png)
![スクリーンショット 2017-05-08 16.20.51.png](https://qiita-image-store.s3.amazonaws.com/0/150615/a7cae5e6-ab2d-144a-645d-f565eaa0af7d.png)

ユーザー名は`A`固定でやってます

# 感想
- `UITableViewDelegate`や`UITableViewDataSource`といったものを設置しなくてもよい
- （最初に処理を予約しておけば）リストの要素を管理しなくてよい
- text入力を自前で監視しなくてよい

と、いいこと（簡単なこと）が多かったです
RxSwiftは奥が深いのでまだまだこれからやっていこうと思います