일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- swift db
- ios database
- Tuist
- arc
- RxSwift
- swift 6
- KeyPath
- Firebase
- xcode
- swift
- SwiftUI
- GCD
- swift database
- Subject
- Swift Tuist
- swift 5.9
- Firebase Analytics
- ribs
- 카카오뱅크 ios
- realm
- ios
- SeSAC
- Combine
- JSON
- 네트워크 통신
- observable
- Tuist Swift
- combinecocoa
- uitableviewdiffabledatasource
- Subscribe
- Today
- Total
천원의 개발
Swift Combine을 활용하여 간단하게 tableView를 그려보자 본문
안녕하세요 천원입니다.
오늘은 Combine을 사용하면서 RxCocoa의 bind 메서드처럼 간편하게 UITableView를 그릴 수 없을까 고민한 내용을 공유드립니다.
1. RxSwift TableView 그리기
let items = Observable.just([
"First Item",
"Second Item",
"Third Item"
])
/// bind 메서드를 활용한 tableView
items.bind(to: tableView.rx.items) { (tableView, row, element) in
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")!
cell.textLabel?.text = "\(element) @ row \(row)"
return cell
}
.disposed(by: disposeBag)
위의 코드와 같이 bind 메서드를 활용해서 간편히 tableView를 그릴 수 있습니다.
2. Combine TableView 그리기
기존에는 UITableViewDataSource를 활용하여 tableView를 구현하였습니다.
// Return the number of rows for the table.
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 0
}
// Provide a cell object for each row.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Fetch a cell of the appropriate type.
let cell = tableView.dequeueReusableCell(withIdentifier: "cellTypeIdentifier", for: indexPath)
// Configure the cell’s contents.
cell.textLabel!.text = "Cell text"
return cell
}
그러나 최근에는 Combine과 함께는 UITableViewDiffableDataSource, NSDiffableDataSourceSnapshot 를 활용하여 코드를 작성하고 있습니다. 자세한 내용은 https://developer.apple.com/videos/play/wwdc2019/220 해당 wwdc 영상을 통하여 확인이 가능합니다.
private func setupDataSource() {
dataSource = UITableViewDiffableDataSource<Int, String>(tableView: tableView) { tableView, indexPath, itemIdentifier in
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = itemIdentifier
return cell
}
}
private func applySnapshot(with items: [String]) {
var snapshot = NSDiffableDataSourceSnapshot<Int, String>()
snapshot.appendSections([0])
snapshot.appendItems(items)
dataSource.apply(snapshot, animatingDifferences: true)
}
이런식으로 매번 tableView를 구현 시 dataSource와 snapshot을 구현해 적용해 줘야하는 불편함이 존재합니다.
3. Combine + UITableViewDiffableDataSource
반복적으로 작성하는 dataSource와 snapShot을 한번에 만들어 줄 메서드를 구현해 봅시다.
우선 DataSoucre 작성을 위해 AnyPublisher 타입의 데이터 리스트를 제네릭하게 받습니다(AnyPublisher<[T], Never>)
또한 초기화 시 필요한 cellProvider 클로저 또한 받아서 DataSource를 구현해 줍니다.
func bind<T: Hashable>(
to publisher: AnyPublisher<[T], Never>,
cellProvider: @escaping (UITableView, IndexPath, T) -> UITableViewCell
) {
let dataSource = UITableViewDiffableDataSource<Int, T>(tableView: self) { tableView, indexPath, item in
return cellProvider(tableView, indexPath, item)
}
}
그런 후 SnapShot을 만들기 위해 item 리스트를 받아와 줘야하니 publisher의 sink 메서드를 통해서 [T] 리스트를 받아와 snapShot을 만들어 줍시다.
func bind<T: Hashable>(
to publisher: AnyPublisher<[T], Never>,
cellProvider: @escaping (UITableView, IndexPath, T) -> UITableViewCell
) {
let dataSource = UITableViewDiffableDataSource<Int, T>(tableView: self) { tableView, indexPath, item in
return cellProvider(tableView, indexPath, item)
}
publisher
.sink { items in
var snapshot = NSDiffableDataSourceSnapshot<Int, T>()
snapshot.appendSections([0])
snapshot.appendItems(items)
dataSource.apply(snapshot, animatingDifferences: true)
}
}
마지막으로 여러 곳에서 편하게 사용할 수 있도록 해당 메서드를 extension으로 적용해 두면,
extension UITableView {
func bind<T: Hashable>(
to publisher: AnyPublisher<[T], Never>,
cellProvider: @escaping (UITableView, IndexPath, T) -> UITableViewCell
) -> AnyCancellable {
let dataSource = UITableViewDiffableDataSource<Int, T>(tableView: self) { tableView, indexPath, item in
return cellProvider(tableView, indexPath, item)
}
return publisher
.sink { items in
var snapshot = NSDiffableDataSourceSnapshot<Int, T>()
snapshot.appendSections([0])
snapshot.appendItems(items)
dataSource.apply(snapshot, animatingDifferences: true)
}
}
}
/// 사용하는 쪽
tableView.bind(to: transaction.eraseToAnyPublisher()) { tableView, indexPath, item in
let cell = tableView.dequeueReusableCell(withIdentifier: DespositCell.identifier, for: indexPath) as! DespositCell
cell.bind(item)
return cell
}
.store(in: &cancellables)
간편하게 Combine을 활용한 간편한 테이블 뷰 구현이 완성됩니다. 🎉
'iOS&Swift🍎 > Combine' 카테고리의 다른 글
Combine Subject, PassthroughSubject, CurrentValueSubject 정리 (2) | 2023.10.12 |
---|