简体   繁体   English

如何使用 rxSwift 和 Moya 从 viewModel 绑定数据?

[英]How to bind data from viewModel in view with rxSwift and Moya?

I'm trying to create an app to get some news from an API and i'm using Moya, RxSwift and MVVM.我正在尝试创建一个应用程序来从 API 获取一些新闻,我正在使用 Moya、RxSwift 和 MVVM。 This is my ViewModel:这是我的视图模型:

import Foundation
import RxSwift
import RxCocoa

public enum NewsListError {
    case internetError(String)
    case serverMessage(String)
}

enum ViewModelState {
    case success
    case failure
}

protocol NewsListViewModelInput {
    func viewDidLoad()
    func didLoadNextPage()
}

protocol MoviesListViewModelOutput {
    var newsList: PublishSubject<NewsList> { get }
    var error: PublishSubject<String> { get }
    var loading: PublishSubject<Bool> { get }
    var isEmpty: PublishSubject<Bool> { get }
}

protocol NewsListViewModel: NewsListViewModelInput, MoviesListViewModelOutput {}

class DefaultNewsListViewModel: NewsListViewModel{

    func viewDidLoad() {

    }

    func didLoadNextPage() {

    }


    private(set) var currentPage: Int = 0
    private var totalPageCount: Int = 1

    var hasMorePages: Bool {
        return currentPage < totalPageCount
    }
    var nextPage: Int {
        guard hasMorePages else { return currentPage }
        return currentPage + 1
    }

    private var newsLoadTask: Cancellable? { willSet { newsLoadTask?.cancel() } }

    private let disposable = DisposeBag()

    // MARK: - OUTPUT
    let newsList: PublishSubject<NewsList> = PublishSubject()
    let error: PublishSubject<String> = PublishSubject()
    let loading: PublishSubject<Bool> = PublishSubject()
    let isEmpty: PublishSubject<Bool> = PublishSubject()

    func getNewsList() -> Void{
        print("sono dentro il viewModel!")
        NewsDataService.shared.getNewsList()
            .subscribe { event in
                switch event {
                case .next(let progressResponse):
                    if progressResponse.response != nil {
                        do{
                            let json = try progressResponse.response?.map(NewsList.self)
                            print(json!)
                            self.newsList.onNext(json!)
                        }
                        catch _ {
                            print("error try")
                        }
                    } else {
                        print("Progress: \(progressResponse.progress)")
                    }
                case .error( _): break
                // handle the error
                default:
                    break
                }
        }
    }

}

This is my ViewController, where xCode give me the following error when i try to bind to tableNews:这是我的 ViewController,当我尝试绑定到 tableNews 时,xCode 给我以下错误:

Expression type 'Reactive<_>' is ambiguous without more context
import UIKit
import RxSwift
import RxCocoa

class ViewController: UIViewController {

    @IBOutlet weak var tableNews: UITableView!

    let viewModel = DefaultNewsListViewModel()

    var disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()

    }

    private func setupBindings() {

        viewModel.newsList.bind(to: tableNews.rx.items(cellIdentifier: "Cell")) {
            (index, repository: NewsList, cell) in
            cell.textLabel?.text = repository.name
            cell.detailTextLabel?.text = repository.url
        }
        .disposed(by: disposeBag)
    }
}

This is the service that get data from API:这是从 API 获取数据的服务:


import Moya
import RxSwift
struct NewsDataService {
    static let shared = NewsDataService()

    private let disposable = DisposeBag()

    private init() {}

    fileprivate let newsListProvider = MoyaProvider<NewsService>()

    func getNewsList() -> Observable<ProgressResponse> {
        self.newsListProvider.rx.requestWithProgress(.readNewsList)
        }
    }

I'm new at rxSwift, I followed some documentation but i'd like to know if i'm approaching in the right way.我是 rxSwift 的新手,我遵循了一些文档,但我想知道我是否以正确的方式接近。 Another point i'd like to know is how correctly bind my tableView to viewModel.我想知道的另一点是如何正确地将我的 tableView 绑定到 viewModel。 Thanks for the support.感谢您的支持。

As @FabioFelici mentioned in the comments, UITableView.rx.items(cellIdentifier:) is expecting to be bound to an Observable that contains an array of objects but your NewsListViewModel.newsList is an Observable<NewsList> .正如@FabioFelici 在评论中提到的, UITableView.rx.items(cellIdentifier:)期望绑定到一个包含对象数组的 Observable ,但您的NewsListViewModel.newsList是一个Observable<NewsList>

This means you either have to extract the array out of NewsList (assuming it has one) through a map.这意味着您必须通过地图从 NewsList 中提取数组(假设它有一个)。 As in newsList.map { $0.items }.bind(to: ...就像在newsList.map { $0.items }.bind(to: ...

Also, your MoviesListViewModelOutput should not be full of Subjects, rather it should contain Observables.此外,您的MoviesListViewModelOutput不应充满主题,而应包含可观察对象。 And I wouldn't bother with the protocols, strut s are fine.而且我不会打扰协议, strut s 很好。

Also, your view model is still very imperative, not really in an Rx style.此外,您的视图模型仍然非常必要,而不是真正的 Rx 风格。 A well constructed Rx view model doesn't contain functions that are repeatedly called.一个构造良好的 Rx 视图模型不包含重复调用的函数。 It just has a constructor (or is itself just a single function.) You create it, bind to it and then you are done.它只有一个构造函数(或者它本身只是一个函数。)你创建它,绑定到它然后你就完成了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM