Reputation: 1854
Let's say we have a class Dog
class Dog {
let age = BehaviorRelay<Int>(value: 1)
}
and somewhere we have an array property with all the dogs
let dogs = BehaviorRelay<[Dog]>(value: [..., ...])
now I want to create UI which needs array of dogs listed in UITableView, and it wants to be updated (call reloadDate under the hood) when age of each dog changes?
So when I simply subscribe like that:
dogs.subscribe(onNext: {
print("\($0)")
})
the subscription will get fired when new dog comes to array, or some leaves array, but not when dogs mature, i.e.:
dogs.value[1].age.accept(2)
I know about flatMap and flatMapLatest, but they seems to expects Dogs to be a plain type, not array. I'm new to RxSwift, any help will be appreciated!
Upvotes: 3
Views: 999
Reputation: 33979
So when you have a setup as described, then the table view itself would be bound to the dogs
observable... dogs.bind(to: tableView.rx.items...
Then inside the closure, you would have the cell bind to the age
observable to display the current age. With all this in place, if you update the age of a particular dog, the cell will automatically update without having to reload the entire tableView. The only time the tableView will reload is when the new dogs are added, or dogs are removed from the array. You can limit even that behavior to just the dog(s) that are added/removed by including a custom data source (or by using the RxDataSources library) to load/unload just the dog(s) that were added/removed.
For reference, here is some example code:
class Dog {
let age = BehaviorRelay<Int>(value: 1)
}
class DogController: UIViewController {
@IBOutlet var tableView: UITableView!
let dogs = BehaviorRelay<[Dog]>(value: [])
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
dogs
.bind(to: tableView.rx.items(cellIdentifier: "Cell", cellType: DogCell.self)) { _, dog, cell in
cell.configure(dog: dog)
}
.disposed(by: disposeBag)
}
}
class DogCell: UITableViewCell {
@IBOutlet var ageLabel: UILabel!
var disposeBag = DisposeBag()
override func prepareForReuse() {
super.prepareForReuse()
disposeBag = DisposeBag()
}
func configure(dog: Dog) {
dog.age
.map { "\($0) years old" }
.bind(to: ageLabel.rx.text)
.disposed(by: disposeBag)
}
}
With the above, the cell that is displaying dog[1] will be listening to the dog's age and if you call dogs.value[1].age.accept(2)
, that cell will notice the change and update itself.
Upvotes: 1