RxDataSourcesを用いたTableViewで特定の条件に該当するセルを編集不可にする方法

# 記事の目的
RxDataSourcesを用いたUITableViewで、特定の条件に該当するセルを編集不可にするのに時間がかかったため、本記事でまとめておこうと思います。

# 実装
セルの編集可否は canEditRowAtIndexPath のクロージャー内で実装します。コードは以下の通りです。

```swift
self.dataSource = RxTableViewSectionedReloadDataSource<SectionOfCustomData>(
            configureCell: { (ds: TableViewSectionedDataSource<SectionOfCustomData>, tableView: UITableView, indexPath: IndexPath, model: CustomData) -> UITableViewCell in
                
                let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
                cell.textLabel?.text = model.str
                return cell
            }, canEditRowAtIndexPath: { datasource, indexPath in
                
                let data = datasource.sectionModels[indexPath.section].items
                
                if data[indexPath.row].str == "zero" {
                    return false
                }
                return true
            }, canMoveRowAtIndexPath: { _, _ in
                return false
            }
        )
```
こちらのコードでは、`data[indexPath.row].str == "zero"` であるセルが編集不可となります。
![画像.GIF](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3631508/7157ab64-ac73-4812-40e8-2407c04d9f71.gif)



<br><br>
コード全体は以下の通りです。
なお、RxDataSourcesを用いてサンプルとなるTableViewを作成する部分の実装は、こちらのサイトのコードを参考にさせていただきました。

https://qiita.com/tiqwab/items/6cf029b424c25da402a2

```swift:ViewController.swift
import UIKit
import RxSwift
import RxCocoa
import RxDataSources

class ViewController: UIViewController, UITableViewDelegate {
    
    @IBOutlet weak var tableView: UITableView!
    
    
    @IBAction func editButton(_ sender: UIButton) {
        self.tableView.setEditing(true, animated: true)
    }
    
    @IBAction func cancelButton(_ sender: UIButton) {
        self.tableView.setEditing(false, animated: true)
    }
    
    var dataSource: RxTableViewSectionedReloadDataSource<SectionOfCustomData>!
    
    let disposeBag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView.tableFooterView = UIView()
        
        tableView.rx.setDelegate(self).disposed(by: self.disposeBag)
        setupDataSource()
        bindModels()
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        navigationController?.setNavigationBarHidden(false, animated: true)
        navigationController?.setToolbarHidden(false, animated: true)
    }
    
    func setupDataSource() {
        self.dataSource = RxTableViewSectionedReloadDataSource<SectionOfCustomData>(
            configureCell: { (ds: TableViewSectionedDataSource<SectionOfCustomData>, tableView: UITableView, indexPath: IndexPath, model: CustomData) -> UITableViewCell in
                
                let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
                cell.textLabel?.text = model.str
                return cell
            }, canEditRowAtIndexPath: { datasource, indexPath in
                
                let data = datasource.sectionModels[indexPath.section].items
                
                if data[indexPath.row].str == "zero" {
                    return false
                }
                return true
            }, canMoveRowAtIndexPath: { _, _ in
                return false
            }
        )
        self.dataSource.titleForHeaderInSection = { ds, index in
            return ds.sectionModels[index].header
        }
    }
    
    func bindModels() {
        let sections = [
            SectionOfCustomData(header: "First section",
                                items: [CustomData(str: "zero"),
                                        CustomData(str: "one")]
                               ),
            SectionOfCustomData(header: "Second section",
                                items: [CustomData(str: "two"),
                                        CustomData(str: "zero"),
                                        CustomData(str: "three")]
                               )
        ]
        Observable.just(sections)
            .bind(to: tableView.rx.items(dataSource: self.dataSource))
            .disposed(by: self.disposeBag)
    }
}
```

```swift:SectionOfCustomData.swift
import Foundation
import RxDataSources

struct CustomData {
    var str: String
}

struct SectionOfCustomData {
    var header: String
    var items: [Item]
}

extension SectionOfCustomData: SectionModelType {
    typealias Item = CustomData

    init(original: SectionOfCustomData, items: [SectionOfCustomData.Item]) {
        self = original
        self.items = items
    }
}
```

# 注意点
上記のコードで、"zero" のセルを編集不可にできましたが、編集モード中に "zero" のセルをタップすることは可能なため、セルタップ時には選択済のセルとしてカウントされます。 
そのため、上記コードを `tableView.allowsMultipleSelectionDuringEditing = true` で使用する場合、セル選択時の処理で `tableView.indexPathsForSelectedRows` から該当のセルのindexPath を取り除く処理が必要です。