
RxDataSource
rxDataSource는 UITableView와 UICollectionView DataSources를 RxSwift와 함께 사용되는 라이브러리이다.
table, CollectionView를 DataSource를 이용해 구현을 하게 되면 간단한 기능이더라도 많은 수의 delegate 메서드들이 필요해 복잡하고 귀찮아진다.
특히 복잡한 데이터관리와 UI와 데이터 동기화, 비동기 데이터 처리부분에서 효율적이다.
이런점들을 RxSwift가 도움을 준다.
let data = Observable<[String]>.just(["first element", "second element", "third element"])
data.bind(to: tableView.rx.items(cellIdentifier: "Cell")) { index, model, cell in
cell.textLabel?.text = model
}
.disposed(by: disposeBag)
//data들을 바인딩하는 방법들
//rx.items(dataSource:protocol<RxTableViewDataSourceType, UITableViewDataSource>)
//rx.items(cellIdentifier:String)
//rx.items(cellIdentifier:String:Cell.Type:_:)
//rx.items(_:_:)
이러한 방법들은 간단한 데이터를 사용할 때는 좋지만 만약 복잡한데이터나, 두 개 이상의 섹션들을 다뤄야 할때는 어려움을 겪을 수 있고 애니메이션구현시도 마찬가지이다.(추가, 수정, 삭제등)
복잡한 데이터를 이용할 때의 경우를 살표보겠습니다.
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, Int>>(configureCell: configureCell)
Observable.just([SectionModel(model: "title", items: [1, 2, 3])])
.bind(to: tableView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
위의 코드를 살표보면 먼저 dataSource를 정의하고(타입 등) cell의 내용을 구성하는 것을 확인할 수 있습니다.
그 후 Observable에 SectionModel에 맞게 데이터를 보내고 이를 바인딩 하는 것을 확인할 수 있습니다.
typealias는 새로운 타입을 만드는 것이 아닌 존재하는 타입에 대해 별칭을 프로그램에 알려주는 것입니다.
typealias StringDictionary<Value> = Dictionary<String, Value> var aliasDictionary: StringDictionary<String> = [:] var originDictionary: Dictionary<String, String> = [:] //alias와 origin 딕셔너리는 실제로 같습니다.
struct CustomData {
var anInt: Int
var aString: String
var aCGPoint: CGPoint
}
struct SectionOfCustomData {
var header: String
var items: [Item]
}
extension SectionOfCustomData: SectionModelType {
typealias Item = CustomData
init(original: SectionOfCustomData, items: [Item]) {
self = original
self.items = items
}
}
//5번에 대한 내용
let dataSource = RxTableViewSectionedReloadDataSource<SectionOfCustomData>(
configureCell: { dataSource, tableView, indexPath, item in
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = "Item \(item.anInt): \(item.aString) - \(item.aCGPoint.x):\(item.aCGPoint.y)"
return cell
})
위 처럼 만든 dataSource 클로저에 커스터마이징을 할 수 있다.
dataSource.titleForHeaderInSection = { dataSource, index in
return dataSource.sectionModels[index].header
}
dataSource.titleForFooterInSection = { dataSource, index in
return dataSource.sectionModels[index].footer
}
dataSource.canEditRowAtIndexPath = { dataSource, indexPath in
return true
}
dataSource.canMoveRowAtIndexPath = { dataSource, indexPath in
return true
}
섹션을 정의하는 부분도 Observable에 section을 보내면 된다.
dataSource.titleForHeaderInSection = { dataSource, index in
return dataSource.sectionModels[index].header
}
dataSource.titleForFooterInSection = { dataSource, index in
return dataSource.sectionModels[index].footer
}
dataSource.canEditRowAtIndexPath = { dataSource, indexPath in
return true
}
dataSource.canMoveRowAtIndexPath = { dataSource, indexPath in
return true
}
dataSource에 애니메이션 기능을 이용하기 위해서는 SectionModelType대신 AnimatableSectionModelType프로토콜을 채택해야한다.
그리고 두개의 프로토콜을 추가적으로 채택을 해야하는데
첫번째는 IdentifiableType이다
IdentifiableType doc
IdentifiableType은 모델 인스턴스를 고유의 식별자로 나타내는 프로토콜이다.(UUID같은..)
두번째는 Equatable이다
Equatable doc
Equatable을 채택하면 rxDataSource가 어떤 cell에 변화가 있었고 그 특정셀에 애니메이션이 작동될수 있도록 도와준다.
특정 셀에 애니메이션을 보여주기 위해서 Equatable과 IdentifiableType을 채택한다.