Here is how i use RxSwift in my code:
My news variable in ViewModel:
var news = PublishSubject<[Article]>()
Extensions for using Rxswift with tableview.
extension HomeViewController {
func designTableView() {
newsTable.separatorStyle = .none
newsTable.showsVerticalScrollIndicator = false
}
func createCellView() {
homeViewModel.news.bind(to: newsTable.rx.items(cellIdentifier: "cell", cellType: NewsCell.self)) { _, news, cell in
cell.newsTitle.text = news.title
cell.newsImage?.sd_setImage(with: URL(string: news.urlToImage ?? ""), placeholderImage: UIImage(systemName: "photo"))
}.disposed(by: disposeBag)
}
func whenNewsSelected() {
newsTable.rx.modelSelected(Article.self).bind { article in
let vc = self.storyboard?.instantiateViewController(withIdentifier: "NewsDetail") as? NewsDetails
vc?.article = article
self.navigationController?.pushViewController(vc!, animated: true)
}.disposed(by: disposeBag)
}
func setDelegateForTableView() {
newsTable.rx.setDelegate(self).disposed(by: disposeBag)
}
}
What I want to do is, adding a Favorite action to cell. I tried the code belove but I don't how can I access the news data on that row.
func tableView(_ tableView: UITableView,
trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
{
let FlagAction = UIContextualAction(style: .normal, title: "Fav", handler: { (_: UIContextualAction, _: UIView, success: (Bool) -> Void) in
print("osman")
success(true)
})
FlagAction.backgroundColor = .orange
return UISwipeActionsConfiguration(actions: [FlagAction])
}
Can someone help me about this?
CodePudding user response:
Instead of var news = PublishSubject<[Article]>()
use let news = BehaviorSubject<[Article]>(value: [])
(Note: Subjects should always be let constants, never var.)
Then when you need access to the articles, you can do:
let articles = (try? homeViewModel.news.value()) ?? []
I don't normally recommend using Subjects but the alternative here is to write your own DataSource type or use RxDataSources and that may be too deep down the rabbit hole for you.
if you do want to go down the rabbit hole
Bring in RxDataSources or write your own data source and make it a member of your view controller:
let dataSource = MyDataSource<Article, NewsCell>(cellIdentifier: "cell") { _, news, cell in
cell.newsTitle.text = news.title
cell.newsImage?.sd_setImage(with: URL(string: news.urlToImage ?? ""), placeholderImage: UIImage(systemName: "photo"))
}
use it like this:
func createCellView() {
homeViewModel.news
.bind(to: newsTable.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
}
Then you can access the articles like this:
let articles = dataSource.elements
Here's what a basic data source looks like. This does everything the default RxCocoa data source does.
class MyDataSource<Element, Cell>: NSObject, UITableViewDataSource, RxTableViewDataSourceType
where Cell: UITableViewCell {
private(set) var elements: [Element] = []
private let cellIdentifier: String
private let configureCell: (Int, Element, Cell) -> Void
init(cellIdentifier: String, configureCell: @escaping (Int, Element, Cell) -> Void) {
self.cellIdentifier = cellIdentifier
self.configureCell = configureCell
}
func tableView(_ tableView: UITableView, observedEvent: RxSwift.Event<[Element]>) {
guard case let .next(elements) = observedEvent else { return }
self.elements = elements
tableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
elements.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? Cell else {
return UITableViewCell()
}
let element = elements[indexPath.row]
configureCell(indexPath.row, element, cell)
return cell
}
}