I'm learning MVVM and RxSwift, and I want to display data from GitHub api and populate into collectionViewDiffableDataSource. But it's not displaying my data, even with my snapshot is already setup to accept my data. This is my code
class FollowersListViewModel {
let searchText = BehaviorRelay<String>(value: "")
let page = BehaviorRelay<Int>(value: 1)
var followers = BehaviorRelay<[FollowerViewModel]>(value: [])
var filterFollowers = BehaviorRelay<[FollowerViewModel]>(value: [])
let hasMoreFollower = BehaviorRelay<Bool>(value: false)
let isLoading = BehaviorRelay<Bool>(value: true)
private let manager: NetworkManager
let disposeBag = DisposeBag()
init(manager: NetworkManager) {
self.manager = manager
}
func fetchFollowers(with username: String) {
isLoading.accept(true)
searchText.asObservable()
.filter { $0.count > 2 }
.throttle(.seconds(3), scheduler: MainScheduler.instance)
.distinctUntilChanged()
.flatMapLatest { query in
self.manager.getFollowers(with: query, page: self.page.value)
}.subscribe { followers in
self.isLoading.accept(false)
self.followers.accept(followers.map { FollowerViewModel(follower: $0)})
print(self.followers.value)
} onError: { error in
print(error)
}.disposed(by: disposeBag)
}
}
class FollowersListVC: UIViewController {
var viewModel: FollowersListViewModel
enum Section { case main }
var collectionView: UICollectionView!
var dataSource: UICollectionViewDiffableDataSource<Section, FollowerViewModel>!
override func viewDidLoad() {
super.viewDidLoad()
setupViewController()
setupSearchController()
setupCollectionView()
setupCollectionViewDataSource()
viewModel.fetchFollowers(with: username)
setupSnapshot()
}
private func setupCollectionViewDataSource() {
dataSource = UICollectionViewDiffableDataSource<Section, FollowerViewModel>(collectionView: collectionView, cellProvider: { (collectionView, indexPath, follower) -> UICollectionViewCell? in
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: FollowersCell.reuseID, for: indexPath) as! FollowersCell
cell.set(followerVM: follower)
return cell
})
}
private func setupSnapshot() {
var snapshot = NSDiffableDataSourceSnapshot<Section, FollowerViewModel>()
snapshot.appendSections([.main])
snapshot.appendItems(viewModel.followers.value)
DispatchQueue.main.async { self.dataSource.apply(snapshot, animatingDifferences: true) }
}
}
I don't know why but it's like my snapshot is not being called, it's seems different when use in MVVM
Your setupSnapshot()
function is being called before the values have been accepted into followers
. I haven't used a NSDiffableDataSourceSnapshot
yet, but you likely need to do something like this instead:
func setupSnapshot() {
viewModel.followers
.map { (followers) in
with(NSDiffableDataSourceSnapshot<Section, FollowerViewModel>()) {
$0.appendSections([.main])
$0.appendItems(followers)
}
}
.observe(on: MainScheduler.instance)
.subscribe(onNext: { [dataSource] snapshot in
dataSource.apply(snapshot, animatingDifferences: true)
})
.disposed(by: disposeBag)
}
The above uses this helper function. It's optional but I think the code looks cleaner when using it:
func with<T>(_ value: T, _ fn: (inout T) -> Void) -> T {
var temp = value
fn(&temp)
return temp
}
By the way...
BehaviorRelay
s should never be var
, always declare them using let
.map
above should be put in your viewModel
rather than here.