본문 바로가기
iOS/ReactorKit

[ReactorKit] RxDataSources로 CollectionView 구현하기

by 0inn 2022. 11. 17.

공부한 것들 기록하려고 합니다 . . !

혼자 공부한 것이므로 틀린 내용이 있을 수 있습니다 . . 

iOS - RxDataSources

프로젝트를 진행하면서 collectionView를 구현하는데 RxDataSources를 사용하였는데요.

기존에 DataSource를 사용하던 방식과 달리 따로 프로토콜을 채택하여 구현해줄 필요없이 데이터 바인딩을 통해 collectionView를 구현할 수 있는 방법입니다 . . !

 

저는 ReactorKit을 사용하고 있기 때문에 RxDataSources를 사용하여 각각의 Cell에 전용 Reactor를 생성하여 Cell이 UI 업데이트를 할 때, 이 Reactor 인스턴스를 받아서 사용하도록 하였습니다.

 

lazy var monthDataSource = DataSource { [weak self] _, collectionView, indexPath, item -> UICollectionViewCell in
    switch item {
    case let .month(reactor):
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: MonthCollectionViewCell.self), for: indexPath) as? MonthCollectionViewCell else { return .init() }
        
        cell.reactor = reactor
        return cell
    }
}

이런식으로 각 cell마다 reactor를 적용했습니다 !

사용방법

1. Cell과 CellReactor를 정의합니다.

import ReactorKit

class MonthCollectionViewCellReactor: Reactor {
    enum Action { }
    enum Mutation {}
    
    struct State {
        var day: String
    }
    
    var initialState: State
    
    init(state: State) {
        self.initialState = state
    }
}

저는 달력을 구현하였기 때문에 해당 일을 받아올 String 타입의 day를 State에 선언하였습니다.

 

import UIKit

import ReactorKit

class MonthCollectionViewCell: BaseCollectionViewCell, View {
    
    // MARK: - Properties
    
    typealias Reactor = MonthCollectionViewCellReactor
    
    // MARK: - UI Components
    
    ...
    
    // MARK: - Methods
    
    ...
    
    func bind(reactor: Reactor) {
        dayLabel.text = reactor.currentState.day
        dayRoundView.isHidden = reactor.currentState.day == ""
    }
}

Cell에서는 bind 메서드를 구현하여 UI를 업데이트 해줍니다.

reactor.currentState를 통해 현재 state를 받아옵니다.

 

2. SectionModel을 정의해줍니다.

import RxDataSources

typealias MonthSectionModel = SectionModel<MonthSection, MonthItem>

enum MonthSection {
    case month([MonthItem])
}

enum MonthItem {
    case month(MonthCollectionViewCellReactor)
}

Section과 Item에서 사용할 때는 enum으로 선언하고, enum으로 구분 후 associate type으로 모델을 선언합니다.

MonthSection은 Section에 사용될 타입이고, MonthItem은 Item에 사용될 타입입니다.

지금 저는 하나의 Section만 사용중이므로 하나만 선언하였습니다.

 

extension MonthSection: SectionModelType {
    typealias Item = MonthItem
    
    var items: [Item] {
        switch self {
        case let .month(items):
            return items
        }
    }
    
    init(original: MonthSection, items: [MonthItem]) {
        switch original {
        case let .month(items):
            self = .month(items)
        }
    }
}

해당 MonthSection은 RxDataSources의 모델로 사용하기 위해 SectionModelType 프로토콜을 준수하며 Item과 items를 정의해줘야 합니다.

 

3. RxDataSources 사용

// MARK: - HomeViewController

// dataSource 생성하고, MonthSectionModel 타입으로 전달
typealias DataSource = RxCollectionViewSectionedReloadDataSource<MonthSectionModel>

lazy var monthDataSource = DataSource { [weak self] _, collectionView, indexPath, item -> UICollectionViewCell in
    switch item {
    case let .month(reactor):
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: MonthCollectionViewCell.self), for: indexPath) as? MonthCollectionViewCell else { return .init() }
        
        cell.reactor = reactor
        return cell
    }
}

func bind(reactor: HomeReactor) {
	// collectionView에 bind 해주기
	reactor.state
		.map(\.monthSections)
		.bind(to: monthView.collectionView.rx.items(dataSource: monthDataSource))
		.disposed(by: disposeBag)
}

RxCollectionViewSectionedReloadDataSource<MonthSectionModel>으로 사용합니다.

collectionView에 해당 MonthSectionModel 객체를 바인딩합니다.

 

// MARK: - HomeReactor

func makeSections() -> [MonthSectionModel] {
    let days = getDays()
    let items = days.map { (day) -> MonthItem in
        return .month(MonthCollectionViewCellReactor(state: .init(day: day)))
    }
    
    let section = MonthSectionModel.init(model: .month(items), items: items)

    return [section]
}

Reactor에서 해당 MonthSectionModel을 Observable로 정의합니다.


참고

https://ios-development.tistory.com/796

https://github.com/RxSwiftCommunity/RxDataSources

'iOS > ReactorKit' 카테고리의 다른 글

[ReactorKit] ReactorKit의 transform() 사용하기  (0) 2022.12.06
[ReactorKit] ReatorKit 살펴보기  (0) 2022.11.10