首页 > 解决方案 > 数据未在 collectionView DiffableDataSource MVVM RxSwift 中显示

问题描述

我正在学习 MVVM 和 RxSwift,我想显示来自 GitHub api 的数据并填充到 collectionViewDiffableDataSource 中。但它没有显示我的数据,即使我的快照已经设置为接受我的数据。这是我的代码

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) }
    }
}

我不知道为什么,但这就像我的快照没有被调用,在 MVVM 中使用时似乎有所不同

标签: swiftmvvmrx-swiftuicollectionviewdiffabledatasourcensdiffabledatasourcesnapshot

解决方案


您的setupSnapshot()函数在值被接受之前被调用followers。我还没有使用过NSDiffableDataSourceSnapshot,但你可能需要做这样的事情:

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)
}

上面使用了这个辅助函数。它是可选的,但我认为使用它时代码看起来更干净:

func with<T>(_ value: T, _ fn: (inout T) -> Void) -> T {
    var temp = value
    fn(&temp)
    return temp
}

顺便一提...

  • BehaviorRelays 永远不应该var,总是使用let.
  • 像这样过度使用 BehaviorRelay 是一种代码异味。
  • map以上应该放在你的而viewModel不是这里。

推荐阅读