[Xcode/Swift] RxSwift × TableViewの実装練習

実装

Model

struct Pokemon {
    var name: String
    var type: String // Typeはお好みでEnumにしても良い気がする
    var level: Int
}

ViewController

import UIKit
import RxSwift
import RxCocoa

final class HomeViewController: UIViewController {
    // MARK: - Properties
    @IBOutlet private weak var tableView: UITableView! {
        didSet {
            tableView.register(UINib(nibName: "HomeTableViewCell", bundle: nil), forCellReuseIdentifier: "HomeTableViewCell")
        }
    }

    private let disposeBag = DisposeBag()
    private let viewModel = HomeViewModel()

    // MARK: - View Life Cycle
    override func viewDidLoad() {
        super.viewDidLoad()
        bind(to: viewModel)
    }

}

// MARK: - Bind
private extension HomeViewController {
    func bind(to viewModel: HomeViewModelType) {
        viewModel.outputs.pokemon
            .drive(tableView.rx.items) { tableView, row, element in
                let indexPath = IndexPath(row: row, section: 0)
                guard let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTableViewCell", for: indexPath) as? HomeTableViewCell else {
                    return UITableViewCell()
                }
                cell.configure(with: element)
                return cell
            }
            .disposed(by: disposeBag)
    }
}

ViewModel

import RxSwift
import RxCocoa

protocol HomeViewModelInputs: AnyObject {
}

protocol HomeViewModelOutputs: AnyObject {
    var pokemon: Driver<[Pokemon]> { get }
}

protocol HomeViewModelType: AnyObject {
    var inputs: HomeViewModelInputs { get }
    var outputs: HomeViewModelOutputs { get }
}

final class HomeViewModel: HomeViewModelType, HomeViewModelInputs, HomeViewModelOutputs {
    // MARK: - Properties
    var inputs: HomeViewModelInputs { return self }
    var outputs: HomeViewModelOutputs { return self }

    // MARK: - Input Sources
    // MARK: - Output Sources
    let pokemon: Driver<[Pokemon]>

    // MARK: - Properties
    // 模擬データ、本番ではAPIからデータ持ってくるとかになりそう。
    private let pokemonArray: [Pokemon] = [
        Pokemon(name: "Pikachu", type: "Electric", level: 10),
        Pokemon(name: "Charmander", type: "Fire", level: 8),
        Pokemon(name: "Bulbasaur", type: "Grass", level: 7),
        Pokemon(name: "Squirtle", type: "Water", level: 9),
        Pokemon(name: "Jigglypuff", type: "Fairy", level: 6),
        Pokemon(name: "Geodude", type: "Rock", level: 12),
        Pokemon(name: "Abra", type: "Psychic", level: 11),
        Pokemon(name: "Machop", type: "Fighting", level: 10),
        Pokemon(name: "Gastly", type: "Ghost", level: 8),
        Pokemon(name: "Snorlax", type: "Normal", level: 15)
    ]
    private let _pokemon = BehaviorRelay<[Pokemon]>(value: [])
    private let disposeBag = DisposeBag()

    // MARK: - Initialize
    init() {
        _pokemon.accept(pokemonArray)
        pokemon = _pokemon.asDriver(onErrorDriveWith: .empty())
    }

}

TableViewCell

import UIKit

final class HomeTableViewCell: UITableViewCell {
    // MARK: - Properties
    @IBOutlet private weak var nameLabel: UILabel!
    @IBOutlet private weak var typeLabel: UILabel!
    @IBOutlet private weak var levelLabel: UILabel!

    // MARK: - Initialize
    override func prepareForReuse() {
        super.prepareForReuse()
        nameLabel.text = nil
        typeLabel.text = nil
        levelLabel.text = nil
    }

    // MARK: - Setup
    func configure(with pokemon: Pokemon) {
        nameLabel.text = pokemon.name
        typeLabel.text = pokemon.type
        levelLabel.text = "Level: \(pokemon.level)"
    }
    
}