RxSwift4で廃止になった Variable のリプレイス

調べてみました😼

# Deprecates Variable in favor of BehaviorRelay
## 4.0.0-rc.0より廃止
https://github.com/ReactiveX/RxSwift/releases/tag/4.0.0-rc.0
> Deprecates Variable in favor of BehaviorRelay.

`Variable`は`BehaviorRelay`に取って代わる形で、廃止になります🙋🍂  

## 廃止の経緯
[Issue #1501](https://github.com/ReactiveX/RxSwift/issues/1501)

* 標準的なクロスプラットフォームのコンセプトではない
    * （[ReactiveX](http://reactivex.io/)の思想にないということ・・・🤔？）
* イベント管理において拡張性が低い
* 命名が`*Relay`に対して一貫していない
* Rx内の他のものと比べてみると、メモリの管理モデルに一貫性がない（`dealloc`時に`Complete`される）

とのこと。

## ただしDeprecation warningsは出ない

* `Variable`はかなり多用されている
* マルチスレッドでの`BehaviorRelay`利用には、**危険なパターン**がある（後述👇）
* どうやって廃止を進めるか明確にはまだ決まっていない

などの理由があるみたいです。

# Variable & BehaviorRelay

## Variable とは
[Deprecated.swift#L170](https://github.com/ReactiveX/RxSwift/blob/master/RxSwift/Deprecated.swift#L170)

* `BehaviorSubject`のラッパー
* `Error`は流れない
* 変数が解放された時に`Complete`が流れる
* 値のアクセスは`value`プロパティの`getter/setter`で行う
* `import RxSwift`で利用

[RxSwift + MVVMアーキテクチャ](https://qiita.com/mafmoff/items/4818c113f203db54c258)で非常に使いやすいAPIです。

## BehaviorRelay とは
[BehaviorRelay.swift](https://github.com/ReactiveX/RxSwift/blob/master/RxCocoa/Traits/BehaviorRelay.swift)

* `BehaviorSubject`のラッパー
* `Error`と`Complete`は流れない
* 値のアクセスは`value`プロパティと`accept(_ event: Element)`で行う
* `import RxCocoa`で利用


#### `accept(_ event: Element)`

```swift:BehaviorRelay.swift
// Accepts `event` and emits it to subscribers
public func accept(_ event: Element) {
    _subject.onNext(event)
}
```

#### `value`プロパティ
```swift:BehaviorRelay.swift
/// Current value of behavior subject
public var value: Element {
    // this try! is ok because subject can't error out or be disposed
    return try! _subject.value()
}
```

# Sample Codes
## Variable

```swift:Playground
import RxSwift

let bag = DisposeBag()
let relay = Variable("🍎")

func bind() {
    relay.asObservable()
        .subscribe(onNext: { value in
            print(value)
        }).disposed(by: bag)
}

// Apples
bind()
relay.value = "🍏"

```

#### Output
```
🍎
🍏
```

## BehaviorRelay
`import RxSwift`は、`DisposeBag`と`MainScheduler`利用のために行なっています。
実際には`MainScheduler`は指定しなくても良いですが、`asDriver()`利用に合わせて便宜的に書いています。

#### asDriver()利用

```swift:Playground
import RxSwift
import RxCocoa

let bag = DisposeBag()
let relay = BehaviorRelay(value: "🍎")

func bind() {
    relay.asDriver()
        .drive(onNext: { value in
            print(value)
        })
        .disposed(by: bag)
}

// Apples
bind()
relay.accept("🍏")
```

#### asObservable()利用

```swift:Playground
import RxSwift
import RxCocoa

let bag = DisposeBag()
let relay = BehaviorRelay(value: "🍎")

func bind() {
    relay.asObservable()
        .observeOn(MainScheduler.instance) 
        .subscribe(onNext: { value in
            print(value)
        })
        .disposed(by: bag)
}

// Apples
bind()
relay.accept("🍏")

```

#### Output

```
🍎
🍏
```

# マルチスレッド + BehaviorRelay に注意
`Variable`では、以下のような書き方で値を更新することができました。

```swift:Playground
let variable = Variable(["🍎"])
variable.value.append("🍏")
```

`BehaviorRelay`では`value`プロパティは**ReadOnly**のため、同じような書き方はできません。


```swift:Playground
let relay = BehaviorRelay(value: ["🍎"])
relay.value.append("🍏") // error: cannot use mutating member on immutable value: 'value' is a get-only property

```

代替案としては以下のようなパターンが考えられます。

```
let relay = BehaviorRelay(value: ["🍎"])
var copy = relay.value
copy.append("🍏")
relay.accept(copy)
```

ですが、これは**マルチスレッドで行う場合、アンチパターン**と言えるでしょう。

なぜなら`relay.value`をリードした後、すぐに別スレッドで`relay.value`に新たな値がセットされる可能性があります。`relay.accept(copy)`の時に意図せずに上書きしてしまうことになります。

```

Thread A：　var copy = relay.value     // relayの値は["🍎"]
Thread B：　relay.accept(["🍏", "🍏"])　 // relayの値は["🍏", "🍏"]
Thread A：　copy.append("🍏") 
         relay.accept(copy)         // relayの値は["🍎", "🍏"]

```


こちらは、[Issue #1501](https://github.com/ReactiveX/RxSwift/issues/1501)にも記載がありますが、**この問題を解決する方法は今の所ない**みたいです。


# Variable の今後
[Issue #1501](https://github.com/ReactiveX/RxSwift/issues/1501)

現状、以下のような想定をされているそうです🤔💡

* `typealias Variable = BehaviorRelay`とし、解放時`Complete`されるのを廃止する
* Deprecation warningsを追加する
* RxSwiftから`Variable`をなくす
* 次のメジャーアップデートでは、少なくとも`Variable`はRxCocoaに移す


# まとめ
### RxCocoaをインポートする違和感
[Issue #1501](https://github.com/ReactiveX/RxSwift/issues/1501)でも指摘されていますが・・・
`BehaviorRelay`が入っている`RxCocoa`には、他にもUI関連のコンポーネントが含まれています。単純な`Variable`の代用としてCleanArchitectureやMVVMなどのUIレイヤー以外で`import RxCocoa`を行うのは、違和感があると思いました。余計なインポートを行なっているように感じるためです。  
とはいえ、現状、この構成がすぐに覆ることはないと思います。

### わたしたちにできること
私たちにできることは、

* 早めにリプレイスについて考える（問題を早めに検知する）
* タイミングを見極める
* マルチスレッドでの操作などチームでルールを決める

などでしょうか。実際に良いルールなどあればぜひ共有いただきたいです🙋
  
`Variable`でできたことが`BehaviorRelay`ではできなくなっている！などの場合は積極的にIssue出しをしましょう。
例えば、RxSwift4のリリース当初、[Can't bind ObservableType to BehaviorRelay](https://github.com/ReactiveX/RxSwift/issues/1466)というIssueが上がっていましたが、こちらはすぐに追加対応されました。

### ちなみに`addDisposableTo`も廃止になったので注意です。

https://github.com/ReactiveX/RxSwift/releases/tag/3.2.0
> Adds Disposable extension disposed(by:) equivalent to addDisposableTo that is meant to replace it in future 4.0 version.


以上です🎉🎉🎉
ありがとうございました😽

# Thanks to
* https://stackoverflow.com/questions/47452582/how-to-use-behaviorrelay-as-an-alternate-to-variable-in-rxswift
* https://github.com/ReactiveX/RxSwift/issues?utf8=%E2%9C%93&q=is%3Aissue+BehaviorRelay