RxSwiftでUITableViewAlertForLayoutOutsideViewHierarchyのWARNINGが出る問題

# 背景

MacのOSやXcodeのバージョン上げた後にアプリをビルドすると唐突に以下のような警告が出るようになりました。

> Warning once only: UITableView was told to layout its visible cells and other contents without being in the view hierarchy (the table view or one of its superviews has not been added to a window). This may cause bugs by forcing views inside the table view to load and perform layout without accurate information (e.g. table view bounds, trait collection, layout margins, safe area insets, etc), and will also cause unnecessary performance overhead due to extra layout passes. Make a symbolic breakpoint at UITableViewAlertForLayoutOutsideViewHierarchy to catch this in the debugger and see what caused this to occur, so you can avoid this action altogether if possible, or defer it until the table view has been added to a window. Table view: ...

なんとなくではありますが「ビューの階層（ウィンドウ）にまだテーブルビューが追加されていないけど、セルやヘッダーのレイアウトの指示がされた。バグの原因や無駄なオーバーヘッドにもなる可能性がある。シンボリックブレーキングポイントを使って原因を探ってくれ」的なことが言いたいんだと思います。

# 環境

* macOS Mojave 10.14.6
* Xcode 11.0
* RxSwift 5.0.1

# 原因

よくRxSwiftで`UITableView`を使う際（適当ではありますが）以下のような実装をしてる方が多いかと思います。

```swift
class TestViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!

    private let disposeBag: DisposeBag = DisposeBag()
    private let stream: Observable<[Int]> = Observable.just([1])

    override func viewDidLoad() {
        super.viewDidLoad()

        stream.asDriver()
            .drive(tableView.rx.items(cellIdentifier: "cell", cellType: SomeCell.self)) { row, element, cell in
                cell.configure(element: element)
            }
            .disposed(by: disposeBag)
    }
}
```

このよくある実装自体が[背景](#背景)で述べたような警告につながるみたいです。しかし、このままでは何がなんだか分からないので、警告文にあるように「Symbolic Breakpoint」で追求していきます。

# Symbolic Breakpointちゃれんじ

以前使ったことがあるような気もするのですが、やり方を一切覚えていなかったのでググりました。今回は備忘録として残しておきたいと思います。まず最初にXcodeの左のウィンドウで右から2番目のタブを選択し、左下にある＋ボタンから「Symbolic Breakpoint...」を選びます。

![Screen Shot 2019-09-30 at 18.13.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277414/20f156c2-fe17-916e-59e5-7d453def5917.png)

すると、以下のようなウィンドウが表示されるので、言われた通りシンボル欄に`UITableViewAlertForLayoutOutsideViewHierarchy`を入力します。

![Screen Shot 2019-09-30 at 18.14.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277414/39ab09ac-d9e5-ec19-0865-f143c432246a.png)

エンターを押すと以下のような状態になると思います。これで準備完了です。

![Screen Shot 2019-09-30 at 18.10.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277414/0b168412-88c1-8dd2-d08c-8cc3fc883f5f.png)

# 実際に動かしてみる

警告が出てた画面を見ると早速停止してくれました。以下のように`ObservableType.subscribeProxyDataSource`内の`object.layoutIfNeeded()`が原因だったようです。

スレッド
![Screen Shot 2019-09-30 at 18.25.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277414/6d32994d-3b42-56db-6c6f-7651c5f11788.png)

コード
![Screen Shot 2019-09-30 at 18.28.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277414/857e065b-8268-1f88-fbdc-ad8aecb20f15.png)

# さてどうする？

結論から言いますと、しばらく待つのが良いのではないかと思います。というのも、すでに公式で[issue](https://github.com/ReactiveX/RxSwift/pull/2076)として認識されていますし、[PR](https://github.com/ReactiveX/RxSwift/pull/2076)も作成されています。

一応、タイミング的には`viewWillAppear()`以降でバインドすると警告が消えるので`viewDidLoad()`に書いてたコードを`viewWillAppear()`や`viewDidAppear()`に移植することで解決はします。が・・・そもそも`viewWillAppear()`や`viewDidAppear()`は複数回呼び出されるということもあって`viewDidLoad()`に書いていたのであまり得策とは言えません。もちろん`take(1)`や`Bool`型のフラグを用いて工夫することで一度しか呼び出されないような実装もできるでしょうが、すでに公式も動いているので待つ方が良いのではないでしょうか。