To finish our conversion from part 1, we will need all changes, available here.
Collection view
Right now we are using old-fashioned collection view implementation, using delegate and data source. I'll simplify this code a lot by using RxSwift and RxCocoa. Let's refactor viewDidLoad
:
import RxCocoa
override func viewDidLoad() {
super.viewDidLoad()
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "cell")
collectionView.delegate = self
view.addSubview(collectionView)
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: view.topAnchor),
collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
api.rx.getImages
.asObservable()
.observe(on: MainScheduler.instance)
.bind(to: collectionView.rx.items(cellIdentifier: "cell", cellType: CollectionViewCell.self)) { (_, element, cell) in
cell.image = element
}
.disposed(by: disposeBag)
}
As you can see, I'm binding items from API to collections items, this binder takes three parameters, cellIdentifier
, cellType
, and cell configurator. And with that, we can remove whole extension ViewController: UICollectionViewDataSource
, we don't need it anymore!
Collection cell
We can simplify collection cell too. Right now code has to hold a reference to the URL session because we want to cancel this request if the collection is reused before the image loads. We will refactor image
variable.
var disposeBag = DisposeBag()
var image: Image! {
didSet {
disposeBag = DisposeBag()
guard let url = image?.url else { return }
let request = URLRequest(url: url)
URLSession.shared.rx.data(request: request)
.compactMap {
UIImage(data: $0)
}
.observe(on: MainScheduler.instance)
.bind(to: imageView.rx.image)
.disposed(by: disposeBag)
}
}
Now, instead of creating and maintaining tasks, I'm using dispose of the bag to cancel the previous request, by creating new bag, the old one is removed from memory, triggering disposing of created observable. After that, I'm creating a new request and execute it using the Rx extension.
Lastly, update reuse method:
override func prepareForReuse() {
super.prepareForReuse()
imageView.image = nil
image = nil
}
And this is it, with Rx we can remove some parts of code, old fashioned data sources and use a more modern, functional style to implement API calls and UI bindings.
Here you can find all RxSwift course posts: