[iOS]いきなり！RxSwift (当方はSwift初心者でいきなりRxSwift！) よく使うRxSwiftライブラリ RxDataSources編

#はじめに
前回までは、カウンターアプリ、WebViewアプリなどのサンプルアプリを実装してきました。今回は、よく使うRxSwift系ライブラリのひとつとしてRxDataSourcesを、実装しながら見ていきたいと思います。

#環境
Xcode10.3
Swift5.0.1
RxSwift 5.0.0
RxCocoa 5.0.0

<今回初めて使うライブラリ>
RxDataSources 4.0.1


#準備

1.プロジェクト作成

-Xcodeを起動 
-Create a new Xcode project 
　＞Single View App
　＞Product Name:RxSwiftLibSample
　＞完了。すぐにプロジェクトを閉じます。

2.ターミナルを起動して、ディレクトリに移動

```
$ cd RxDataSourcesSample
```

3.Podfile作成/編集

```
$ pod init
$ vi Podfile
```

```:Podfile
# platform :ios, '9.0'

target 'RxDataSourcesSample' do
  use_frameworks!

  pod 'RxSwift'
  pod 'RxCocoa' 
  pod 'RxDataSources'
end
````


4.ライブラリのインストール

```
$ pod install
```

5.プロジェクトを開く
.xcworkspaceから起動する


#Storyboardを削除

1.Main.storyboardの削除
/Main.storyboardをDelete > Move to Trash

2.Info.plist
Info.plistを開く ＞ Main storyboard file base nameの項目を削除(マイナスボタン)

3.AppDelegateの修正

```AppDelegate.swift

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
    /* 追加 ここから */
    self.window = UIWindow(frame: UIScreen.main.bounds)
    let navigationController = UINavigationController(rootViewController: ViewController())
    self.window?.rootViewController = navigationController
    self.window?.makeKeyAndVisible()
    /* 追加 ここまで */
    
    return true
}
```

4.ViewController.xibの作成
- New File ＞ View ＞ Save As: ViewController.xib ＞ Create
- ViewController.xibを開く
- Placeholders > File's Ownerを選択
- ClassにViewControllerを指定
- OutletのviewとXibのViewを接続
- Build & Run > 成功でOK

#実装

```SettingsViewModel.swift
import Foundation
import RxSwift
import RxCocoa
import RxDataSources

class SettingsViewModel {
    
    let items = BehaviorRelay<[SettingsSectionModel]> (value: [])
    
    var itemsObservable: Observable<[SettingsSectionModel]> {
        return items.asObservable()
    }
    
    func setup() {
        updateItems()
    }
    
    func updateItems() {
        let sections: [SettingsSectionModel] = [
            accountSection(),
            commonSection()
        ]
        items.accept(sections)
    }
    
    private func accountSection() -> SettingsSectionModel {
        let items: [SettingsItem] = [
            .account,
            .security,
            .notification,
            .contents
        ]
        return SettingsSectionModel(model: .account, items: items)
    }
    
    private func commonSection() ->  SettingsSectionModel {
        let items: [SettingsItem] = [
            .sounds,
            .dataUsing,
            .accessibility,
            .description(text: "基本設定はこの端末でログインしている全てのアカウントに適用されます。")
        ]
        return SettingsSectionModel(model: .common, items: items)
    }
}
```

```SettingsSectionModel.swift
import Foundation
import UIKit
import RxDataSources

typealias SettingsSectionModel = SectionModel<SettingsSection, SettingsItem>

enum SettingsSection {
    case account
    case common
    case other
    
    var headerHeight: CGFloat {
        return 40.0
    }
    
    var footerHeight: CGFloat {
        return 1.0
    }
}

enum SettingsItem {
    //account section
    case account
    case security
    case notification
    case contents
    
    //common section
    case sounds
    case dataUsing
    case accessibility
    
    //other
    case description(text: String)
    
    var title: String? {
        switch self {
        case .account:
            return "アカウント"
        case .security:
            return "セキュリティ"
        case .notification:
            return "通知"
        case .contents:
            return "コンテンツ設定"
        case .sounds:
            return "サウンド設定"
        case .dataUsing:
            return "データ利用時の設定"
        case .accessibility:
            return "アクセシビリティ"
        case .description:
            return nil
        }
    }
    
    var rowHeight: CGFloat {
        switch self {
        case .description:
            return 72.0
        default:
            return 48.0
        }
    }
    
    var accessoryType: UITableViewCell.AccessoryType {
        switch self {
        case .account, .security, .notification, .contents, .sounds, .dataUsing, .accessibility:
            return .disclosureIndicator
        case .description:
            return .none
        }
    }
}

```

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

class ViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!
    
    private var disposeBag = DisposeBag()
    
    private lazy var dataSource = RxTableViewSectionedReloadDataSource<SettingsSectionModel>(configureCell: configureCell)
    
    private lazy var configureCell: RxTableViewSectionedReloadDataSource<SettingsSectionModel>.ConfigureCell =
    {   [weak self] (dataSource, tableView, indexPath, _) in
        let item = dataSource[indexPath]
        
        switch item {
        case .account, .security, .notification, .contents, .sounds, .dataUsing, .accessibility:
            let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
            cell.textLabel?.text = item.title
            cell.accessoryType = item.accessoryType
            return cell
            
            
        case .description(let text):
            let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
            cell.textLabel?.text = text
            cell.isUserInteractionEnabled = false
            return cell
        }
    }
    
    private var viewModel: SettingsViewModel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupViewController()
        setupTableView()
        setupViewModel()
    }

    private func setupViewController()
    {
        navigationItem.title = "設定"
    }
    
    private func setupTableView()
    {
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        tableView.contentInset.bottom = 12.0
        tableView.backgroundColor = UIColor(red: 0.9, green: 0.9, blue: 0.9, alpha: 1.0)
        tableView.rx.setDelegate(self).disposed(by: disposeBag)
        tableView.rx.itemSelected
            .subscribe(onNext: { [weak self] indexPath in
                guard let item = self?.dataSource[indexPath] else { return }
                self?.tableView.deselectRow(at: indexPath, animated: true)
                switch item {
                case .account:
                    //遷移させる処理
                    //コンパイルエラー回避のためにbreakを書いていますが処理を書いていればbreakは入りません
                    break
                case .security:
                    //遷移させる処理
                    break
                case .notification:
                    //遷移させる処理
                    break
                case .contents:
                    //遷移させる処理
                    break
                case .sounds:
                    //遷移させる処理
                    break
                case .dataUsing:
                    //遷移させる処理
                    break
                case .accessibility:
                    //遷移させる処理
                    break
                case .description:
                    break
                }
            })
            .disposed(by: disposeBag)
    }
    
    private func setupViewModel()
    {
        viewModel = SettingsViewModel()
        
        viewModel.items
            .bind(to: tableView.rx.items(dataSource: dataSource))
            .disposed(by: disposeBag)
        
        viewModel.updateItems()
    }
}

extension ViewController : UITableViewDelegate {
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        let item = dataSource[indexPath]
        return item.rowHeight
    }
    
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        let section = dataSource[section]
        return section.model.headerHeight
    }
    
    func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
        let section = dataSource[section]
        return section.model.footerHeight
    }
    
    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let headerView = UIView()
        headerView.backgroundColor = .clear
        return headerView
    }
    
    func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
        let footerView = UIView()
        footerView.backgroundColor = .clear
        return footerView
    }
}

```

###lazy プロパティ
・参照されるときに初めて初期値が設定されるプロパティ
・レイジープロパティに代入する値は、変数に限らず、メソッドを呼び出して値を設定することができる


[備考]
・クロージャを使ってレイジープロパティを設定することもできる

```
var price = 5000
 
class MyItem {
    lazy var price:Int = { price * 2 }()
}
```