I have the following MVVM-C + RxSwift code.
The problem is that the TableView is not receiving any signals. When I
debug the results I can see that the API call is returning what it should, the objects
array is populated with objects but the tableview does not show any results. Here is the console output:
2018-11-13 16:12:08.107: searchText -> Event next(qwerty)
Search something: qwerty
2018-11-13 16:12:08.324: viewModel.data -> Event next([])
Could it be the tableview itself? Maybe wrong custom cell setup?
ViewController.swift:
tableView = UITableView(frame: self.view.frame)
tableView.delegate = nil
tableView.dataSource = nil
tableView.register(SearchResultCell.self, forCellReuseIdentifier: "SearchResultCell")
viewModel.data
.debug("viewModel.data", trimOutput: false)
.drive(tableView.rx.items(cellIdentifier: "SearchResultCell")) { row, object, cell in
cell.name.text = object.name
cell.something.text = object.something
}
.disposed(by: disposeBag)
ViewModel.swift:
let disposeBag = DisposeBag()
var searchText = BehaviorRelay(value: "something to search for")
lazy var data: Driver<[Object]> = {
return self.searchText.asObservable()
.debug("searchText", trimOutput: false)
.throttle(0.3, scheduler: MainScheduler.instance)
.distinctUntilChanged()
.flatMapLatest(searchSomething)
.asDriver(onErrorJustReturn: [])
}()
func searchSomething(query: String) -> Observable<[Object]> {
print("Search something: \(query)")
let provider = MoyaProvider<APIService>()
var objects = [Object]()
provider.rx.request(.search(query: query)).subscribe { event in
switch event {
case let .success(response):
do {
let responseJSON: NSDictionary = try (response.mapJSON() as? NSDictionary)!
objects = self.parse(json: responseJSON["results"] as Any)
} catch(let error) {
print(error)
}
break
case let .error(error):
print(error)
break
}
}
.disposed(by: disposeBag)
let result: Observable<[Object]> = Observable.from(optional: objects)
return result
}
flatMap
, you do not want to create nested subscriptions. You will create an Observable
that returns the expected result, and flatMap will take care of subscribing to it. In the current state of things, searchSomething
will always return an empty array, as Observable.from(optional: objects)
will be called before the request has a chance to complete.searchSomething
, hence the network request won't have time to finish. Moving provider's declaration to the view model's level solves this issue.Here's searchSomething(query: String) -> Observable<[Object]>
rewritten.
let provider = MoyaProvider<APIService>()
func searchSomething(query: String) -> Observable<[Object]> {
print("Search something: \(query)")
return provider.rx.request(.search(query: query)).map { (response) -> [Object] in
let responseJSON: NSDictionary = try (response.mapJSON() as? NSDictionary)!
return self.parse(json: responseJSON["results"] as Any)
}
}
Instead of doing the transformation in subscribe, it's done in map
, which will be called for every next
event, being passed the value associated with the event.