ReactiveSwift Refresh data - ios

I beginner in ReactiveSwift. This is fetching code in my view model :
private let viewDidLoadProperty = MutableProperty<Void?>(nil)
public func viewDidLoad() {
disposables += self.weatherFetcher.fetchCurrentWeather().startWithResult { (result) in
switch result {
case .success(let value):
_ = value?.list?
.map { [weak self] weatherData in
if let weather = weatherData.weather?.first {
self?.weatherFetcher.fetchWeatherImage(icon: weather.icon).startWithResult { (result) in
switch result {
case .success(let iconData):
self?.cellViewModels.append(WeatherCellViewModel(with: weatherData, iconData: iconData))
case .failure(let error):
print("something went wrong - \(error)")
}
}
} else {
self?.cellViewModels.append(WeatherCellViewModel(with: weatherData, iconData: nil))
}
}
case .failure(let error):
print(error)
}
}
self.viewDidLoadProperty.value = ()
}
When viewDidLoad is called in ViewController then view model starts fetching data. How to tell VC that fetch is end and refreshData can be called? Is any possibility to catch end of viewDidLoad func, I mean after fetching.
initCode :
init(weatherFetcher: WeatherFetcher) {
self.weatherFetcher = weatherFetcher
didStartLoadingWeather = self.viewDidLoadProperty.signal.skipNil()
}

I would first of all advise you on using a ViewModel, that would be in charge of doing these operations in behalf of the UIViewController.
Answering your question directly. You will have to use some sort of mechanism to hold to the data. This can be either a Property or MutableProperty. My advice is for the former. You will also need a trigger, so when viewDidLoad happens, you can communicate this. Assuming you have a ViewModel:
import ReactiveSwift
import enum Result.NoError
public enum ViewState<T> {
case loading
case loaded([T])
case failure(YourError)
}
class ViewModel {
private let (signal, observer) = Signal<Void, NoError>.pipe()
let state: Property<ViewState<WeatherCellViewModel>>
init() {
let fetch = signal.flatMap(.latest, transform: fetcher)
self.state = Property.init(initial: .loading then: fetch)
}
func fetch() {
observer.send(value: ())
}
}
I will leave the completion of the fetch for you. But:
This approach allows you to keep state around (via a property) and allow for an external trigger.
You would now read the values from the state.
the fetcher is created at initialization time and only triggered after fetch function is called.

A good practice for loading table views using ReactiveSwift (if that's your case) is that just simply bind your table view data to a MutableProperty<[your data]>
you just simply fetch your data and when the value is received, reload table view. After that, table view will be magically refreshed.
in your view model:
struct WeatherInfo {
var temp: Int
var icon: String
}
var data: MutableProperty<[WeatherInfo]>([])
func fetch() -> SignalProducer<Bool, SomeError> {
return weatherFetcher.fetchCurrentWeather().on(value: { val in
let mapped = myCustomMapFunc(val) //transforms 'val' to [WeatherInfo]
self.data.swap(mapped)
})
}
in your view controller:
let viewModel = MyViewModel()
func viewDidLoad() {
viewModel.fetch().startWithCompleted {
self.tableView.reloadData()
}
}
public override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewModel.data.value.count
}
public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCell(withIdentifier: "myCellId", for:indexPath) as! MyCustomCell
let info = viewModel.data.value[indexPath.row]
cell.txtTemp.text = info.temp
cell.icon = info.icon
return cell
}
If it's not the table view, just keep the idea and do it on whatever you want. For example, if it's a simple cell, load the cell with new data:
in your view model:
// var data: MutableProperty<WeatherInfo>([]) //you don't need it anymore
func fetch() -> SignalProducer<WethearInfo, SomeError> {
return weatherFetcher.fetchCurrentWeather().map({
return myCustomMapFunc($0) //transforms the input into WeatherInfo
})
}
in your view controller:
let viewModel = MyViewModel()
func viewDidLoad() {
viewModel.fetch().startWithValues { val in
self.reloadMyCell(info: val)
}
}
func reloadMyCell(info: WeatherInfo) {
mycell.temp = info.temp
mycell.icon = info.icon
}

Related

How to send Json Data to Table View Array? Swift

I've been researching and wrecking my brain attempting to get my JSON data to load into my tableview. I've tried placing the data in a Variable & I'm able to see the data in the console when I print it, however unable to push it to my table view.
Am I doing something wrong on the data page or am I not properly accessing the data within the loop?
I've tried putting the loop in the viewdidload but haven't been successful either.
// ViewController
import Foundation
import UIKit
import SDWebImage
class EntertainmentViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var A = EntertainmentApi()
var data = [EntertainmentPageData]()
var AA = EntertainmentApi().userFeedPosts
#IBOutlet weak var entPostTableView: UITableView!
override func viewDidLoad() {
func showTable() {
}
entPostTableView.register(EntertainmentViewrTableViewCell.nib(), forCellReuseIdentifier: EntertainmentViewrTableViewCell.identifier)
entPostTableView.delegate = self
entPostTableView.dataSource = self
super.viewDidLoad()
DispatchQueue.main.async {
self.entPostTableView.reloadData()
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let customCell1 = tableView.dequeueReusableCell(withIdentifier: EntertainmentViewrTableViewCell.identifier, for: indexPath) as! EntertainmentViewrTableViewCell
customCell1.profileDisplayName.text = AA[indexPath.row].postDisplayName
self.AA.forEach({ (EntertainmentPageData) in
customCell1.configue(with: EntertainmentPageData.postDisplayName, PostImage: EntertainmentPageData.imageURLString, PostDescription: EntertainmentPageData.postDescription)
})
return customCell1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func item(for index: Int) -> EntertainmentPageData {
return data[index]
}
func numberOfItems() -> Int {
return data.count
}
}
//Data
import SwiftUI
import SDWebImage
public protocol EntertainmentPagePostItem {
/// The image for the card.
var imageURLString: String { get }
/// Rating from 0 to 5. If set to nil, rating view will not be displayed for the card.
var postDescription: String? { get }
/// Will be displayed in the title view below the card.
var postDisplayName: String { get }
}
public protocol EntertainmentPagePostDataSource: class {
/// CardSliderItem for the card at given index, counting from the top.
func item(for index: Int) -> EntertainmentPagePostItem
/// Total number of cards.
func numberOfItems() -> Int
}
struct HomePagePost: Codable {
var displayName: String
var cityStatus: String
var displayDescription: String
var displayImageURL: String
var lookingFor: String
var profileImager1: String?
var profileImager2: String?
var profileImager3: String?
var profileImager4: String?
}
struct EntertainmentPageData: Codable {
let postDisplayName: String
let imageURLString: String
let postDescription: String?
}
public class entPostFly: Codable {
let postDisplayName, imageURLString, postDescription: String
}
struct eItem: EntertainmentPagePostItem {
var postDisplayName: String
var imageURLString: String
var postDescription: String?
}
public class EntertainmentApi {
var userFeedPosts = [EntertainmentPageData]()
init() {
load()
}
func load() {
guard let apiURL = URL(string: "https://api.quickques.com/....") else {
return
}
let task: () = URLSession.shared.dataTask(with: apiURL) { Data, apiResponse, error in
guard let Data = Data else { return }
do {
let entPostData = try JSONDecoder().decode([EntertainmentPageData].self, from: Data)
self.userFeedPosts = entPostData
}
catch {
let error = error
print(error.localizedDescription)
}
}.resume()
}
func getFeedPosts(completion: #escaping ([EntertainmentPageData]) -> () ) {
guard let apiURL = URL(string: "https://api.quickques.com/....") else {
return
}
let task: () = URLSession.shared.dataTask(with: apiURL) { Data, apiResponse, error in
guard let Data = Data else { return }
do {
let entPostData = try JSONDecoder().decode([EntertainmentPageData].self, from: Data)
completion(entPostData)
}
catch {
let error = error
print(error.localizedDescription)
}
}.resume()
}
}
class Api {
func getHomePagePosts(completion: #escaping ([HomePagePost]) -> Void ) {
guard let apiURL = URL(string: "https://api.quickques.com/.....") else {
return
}
let task: () = URLSession.shared.dataTask(with: apiURL) { Data, apiResponse, error in
guard let Data = Data else { return }
do {
let homePostData = try JSONDecoder().decode([HomePagePost].self, from: Data)
completion(homePostData)
}
catch {
let error = error
print(error.localizedDescription)
}
}.resume()
}
func getImageData(from url: URL, completion: #escaping (Data?, URLResponse?, Error?) -> ()) {
URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
}
}
func getTopMostViewController() -> UIViewController? {
var topMostViewController = UIApplication.shared.keyWindow?.rootViewController
while let presentedViewController = topMostViewController?.presentedViewController {
topMostViewController = presentedViewController
}
return topMostViewController
}
First you have an empty function showTable inside your viewDidLoad - This does nothing. Presumably it is something hanging around from your various attempts. Delete that.
As you have probably worked out, your network fetch operation is going to occur asynchronously and you need to reload the table view once the data has been fetched.
You have some code in viewDidLoad that kind of tries to do this, but it isn't related to the fetch operation. It is just dispatched asynchronously on the next run loop cycle; This is probably still before the data has been fetched.
However, even if the data has been fetched, it won't show up because you are assigning userFeedPosts from a second instance of your API object to AA at initialisation time. This array is empty and will remain empty since Swift arrays are value types, not reference types. When userFeedPosts is updated, AA will hold the original empty array.
To load the data you need to
Start a load operation when the view loads
Pass a completion handler to that load operation to be invoked when the load is complete
Reload your table view with the new data
class EntertainmentViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var data = [EntertainmentPageData]()
#IBOutlet weak var entPostTableView: UITableView!
override func viewDidLoad() {
entPostTableView.register(EntertainmentViewrTableViewCell.nib(), forCellReuseIdentifier: EntertainmentViewrTableViewCell.identifier)
entPostTableView.delegate = self
entPostTableView.dataSource = self
super.viewDidLoad()
EntertainmentAPI.getFeedPosts { result in
DispatchQueue.main.async { // Ensure UI updates on main queue
switch result {
case .error(let error):
print("There was an error: \(error)")
case .success(let data):
self.data = data
self.entPostTableView.reloadData
}
}
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let customCell1 = tableView.dequeueReusableCell(withIdentifier: EntertainmentViewrTableViewCell.identifier, for: indexPath) as! EntertainmentViewrTableViewCell
let post = data[indexPath.row)
customCell1.profileDisplayName.text = data[indexPath.row].postDisplayName
customCell1.configure(with: post.postDisplayName, PostImage: post.imageURLString, PostDescription: post.postDescription)
return customCell1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
}
public class EntertainmentAPI {
static func getFeedPosts(completion: #escaping ((Result<[EntertainmentPageData],Error>) -> Void) ) {
guard let apiURL = URL(string: "https://api.quickques.com/....") else {
return
}
let task = URLSession.shared.dataTask(with: apiURL) { data, apiResponse, error in
if let error = error {
completion(.failure(error))
return
}
guard let data = data else {
/// TODO - Invoke the completion handler with a .failure case
return
}
do {
let entPostData = try JSONDecoder().decode([EntertainmentPageData].self, from: Data)
completion(.success(entPostData))
}
catch {
completion(.failure(error))
}
}.resume()
}
}

Swift - How do I decode json from a REST API

I am trying to make a GET from a REST API in swift. When I use the print statement (print(clubs)) I see the expected response in the proper format. But in the VC is gives me an empty array.
Here is the code to talk to the API
extension ClubAPI {
public enum ClubError: Error {
case unknown(message: String)
}
func getClubs(completion: #escaping ((Result<[Club], ClubError>) -> Void)) {
let baseURL = self.configuration.baseURL
let endPoint = baseURL.appendingPathComponent("/club")
print(endPoint)
API.shared.httpClient.get(endPoint) { (result) in
switch result {
case .success(let response):
let clubs = (try? JSONDecoder().decode([Club].self, from: response.data)) ?? []
print(clubs)
completion(.success(clubs))
case .failure(let error):
completion(.failure(.unknown(message: error.localizedDescription)))
}
}
}
}
and here is the code in the VC
private class ClubViewModel {
#Published private(set) var clubs = [Club]()
#Published private(set) var error: String?
func refresh() {
ClubAPI.shared.getClubs { (result) in
switch result {
case .success(let club):
print("We have \(club.count)")
self.clubs = club
print("we have \(club.count)")
case .failure(let error):
self.error = error.localizedDescription
}
}
}
}
and here is the view controller code (Before the extension)
class ClubViewController: UIViewController {
private var clubs = [Club]()
private var subscriptions = Set<AnyCancellable>()
private lazy var dataSource = makeDataSource()
enum Section {
case main
}
private var errorMessage: String? {
didSet {
}
}
private let viewModel = ClubViewModel()
#IBOutlet private weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.subscriptions = [
self.viewModel.$clubs.assign(to: \.clubs, on: self),
self.viewModel.$error.assign(to: \.errorMessage, on: self)
]
applySnapshot(animatingDifferences: false)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.viewModel.refresh()
}
}
extension ClubViewController {
typealias DataSource = UITableViewDiffableDataSource<Section, Club>
typealias Snapshot = NSDiffableDataSourceSnapshot<Section, Club>
func applySnapshot(animatingDifferences: Bool = true) {
// Create a snapshot object.
var snapshot = Snapshot()
// Add the section
snapshot.appendSections([.main])
// Add the player array
snapshot.appendItems(clubs)
print(clubs.count)
// Tell the dataSource about the latest snapshot so it can update and animate.
dataSource.apply(snapshot, animatingDifferences: animatingDifferences)
}
func makeDataSource() -> DataSource {
let dataSource = DataSource(tableView: tableView) { (tableView, indexPath, club) -> UITableViewCell? in
let cell = tableView.dequeueReusableCell(withIdentifier: "ClubCell", for: indexPath)
let club = self.clubs[indexPath.row]
print("The name is \(club.name)")
cell.textLabel?.text = club.name
return cell
}
return dataSource
}
}
You need to apply a new snapshot to your table view once you have fetched the clubs. Your current subscriber simply assigns a value to clubs and nothing more.
You can use a sink subscriber to assign the new clubs value and then call applySnapshot. You need to ensure that this happens on the main queue, so you can use receive(on:).
self.subscriptions = [
self.viewModel.$clubs.receive(on: RunLoop.main).sink { clubs in
self.clubs = clubs
self.applySnapshot()
},
self.viewModel.$error.assign(to: \.errorMessage, on: self)
]

app crash when success initialize data MVVM

my apps crash when I try to passing the data into another controller but it was successfully load data in my viewController. it's kinda weird though because the data successfully display in my label and my imageView in my controller, what can I suspect is the data is not yet being load. but if the data not being load why the imageView display my data, here I show you my code.
// This is my schoolListViewModel
class SchoolListViewModel {
private(set) var schoolListViewModel = [SchoolViewModel]()
private let services: ProfileServices
private let schools = UserDefaults.getSelectedSchool()
var count: Int {
return schoolListViewModel.count
}
var id: String {
selectedSchoolID()
}
var imageUrl: String {
selectedSchoolImageURL()
}
var schoolName: String {
selectedSchoolName()
}
var schoolCity: String {
selectedSchoolCity()
}
init(services: ProfileServices) {
self.services = services
}
}
extension SchoolListViewModel {
func loadData(success: #escaping (() -> Void), failure: #escaping ((String) -> Void)) {
services.fetchSchoolList { [weak self] result in
switch result {
case .success(let schools):
self?.schoolListViewModel = schools.compactMap({ SchoolViewModel(school: $0)
})
success()
case .failure(let error):
failure(error.localizedDescription)
}
}
}
}
// this is in my profileViewController
var schoolListViewModel: SchoolListViewModel!
private let profileServices = ProfileServices()
// this is my viewModel in my viewDidLoad
progressHUD.show(in: self.view)
schoolListViewModel = SchoolListViewModel(services: profileServices)
schoolListViewModel.loadData(success: {
self.progressHUD.dismiss(animated: true)
self.populateSchool()
}) { [weak self] error in
self?.showHUDWithError(error)
}
schoolSelectionBtn.addTarget(self, action: #selector(handlePickSchool), for: .touchUpInside)
// This is my objc function
private func showHUDWithError(_ error: String) {
progressHUD.textLabel.text = "Error"
progressHUD.detailTextLabel.text = error
progressHUD.dismiss(afterDelay: 4)
}
#objc private func handlePickSchool() {
let selectSchoolVC = SelectSchoolPopUpVC()
selectSchoolVC.schoolListViewModel = schoolListViewModel
present(selectSchoolVC, animated: true, completion: nil)
}
// this is in my SchoolPopUpVC controller
var schoolListViewModel: SchoolListViewModel!
// this is where the error goes in my schoolPopUpVC tableViewDataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return schoolListViewModel.count
}
this where when I try to passing the data, the error show in my tableView numberOfRowsInSection
unexpected found nil when try to unwrapping optional value
You are using an implicitly unwrapped optional for schoolListViewModel but you don't assign a value until viewDidLoad executes. As it's name implies, that method executes after the view is loaded. You refer to schoolListViewModel in numberOfRowsInSection which will be called after the view is loaded but before viewDidLoad has executed, so you get a crash.
The simple fix is to conditionally unwrap schoolListViewModel in that method:
return schoolListViewModel?.count ?? 0

UICollectionView reloads data without calling reloadData()

I have structured my app using the MVVM pattern, the datasource of the collectionView gets data from the viewModel.
In viewModel I have a closure which gets called after updating the data, it passes IndexSet of sections and [IndexPath] of items which collectionView should insert. However I get the crash everytime after calling the insert method with error:
'Invalid update: invalid number of sections. The number of sections contained in the collection view after the update (11) must be equal to the number of sections contained in the collection view before the update (11), plus or minus the number of sections inserted or deleted (11 inserted, 0 deleted).'
I understand what this error means, however I noticed that this is the order in which the methods are called
1. viewDidLoad()
2. numberOfSections(in collectionView: UICollectionView) // returns 0
3. update?(insertedSectionsSet, insertedRows) // the closure gets called in viewModel, insertedSectionsSet has 11 elements, that's correct
4. numberOfSections(in collectionView: UICollectionView) // returns 11, that's correct but it should be called after next point
5. viewModel.update = { [weak self] sections, items in
DispatchQueue.main.async {
self?.collectionView.performBatchUpdates({
self?.collectionView.insertSections(sections) // crash
self?.collectionView.insertItems(at: items)
}, completion: nil)
}
}
As you can see in the code below, I added prints before calling each methods and you can clearly see that numberOfSections gets called after calling the closure and before performing it. It makes absolutely no sense to me why is it happening. I believe that the cause of crash lies in calling the numberOfSections before inserting them, because then it expects 22 sections after inserting.
update
numberofsections
closure
numberofsections
Code:
class PlaylistsDataSource: NSObject, UICollectionViewDataSource {
...
func numberOfSections(in collectionView: UICollectionView) -> Int {
print("numberofsections")
return viewModel.numberOfSections
}
...
}
class PlaylistsMasterViewController: UIViewController {
...
viewDidLoad() {
viewModel.update = { [weak self] sections, items in
DispatchQueue.main.async {
print("closure")
self?.collectionView.performBatchUpdates({
self?.collectionView.insertSections(sections)
self?.collectionView.insertItems(at: items)
}, completion: nil)
}
}
}
...
}
class PlaylistsMasterViewModel {
private var sectionIndexes = [String]()
private var playlistsDictionary = [String: [AWPlaylist]]()
var update: ((IndexSet, [IndexPath]) -> Void)?
var numberOfSections: Int {
return sectionIndexes.count
}
EDIT: Added more code
extension PlaylistsMasterViewModel {
func fetchPlaylists() {
repo.getAllPlaylists(from: [.iTunes]) { [weak self] result in
switch result {
case .success(let playlists):
self?.sortIncoming(playlists: playlists)
case .failure(let error):
print(error.localizedDescription)
}
}
}
private func sortIncoming(playlists: [AWPlaylist]) {
var insertedPlaylists = [(key: String, list: AWPlaylist)]()
var insertedIndexes = [String]()
func insertNewSection(playlist: AWPlaylist, key: String) {
insertedIndexes.append(key)
playlistsDictionary.updateValue([playlist], forKey: key)
}
func insertNewRow(playlist: AWPlaylist, key: String) {
guard var value = playlistsDictionary[key] else {
print("Oh shit")
return
}
value.append(playlist)
value.sort(by: SortingPredicates.Playlist.nameAscending)
playlistsDictionary.updateValue(value, forKey: key)
insertedPlaylists.append((key, playlist))
}
for list in playlists {
let name = list.localizedName.uppercased().trimmingCharacters(in: CharacterSet.whitespaces)
guard let firstCharacter = name.first else { return }
let firstLetter = String(firstCharacter)
let key: String
if CharacterSet.english.contains(firstLetter.unicodeScalars.first!) {
key = firstLetter
} else if CharacterSet.numbers.contains(firstLetter.unicodeScalars.first!) {
key = "#"
} else {
key = "?"
}
if playlistsDictionary[key] == nil {
insertNewSection(playlist: list, key: key)
} else {
insertNewRow(playlist: list, key: key)
}
}
sectionIndexes.append(contentsOf: insertedIndexes)
sectionIndexes.sort(by: { $0 < $1 })
let insertedSections = insertedIndexes.compactMap { index -> Int? in
guard let sectionIndex = self.sectionIndexes.firstIndex(of: index) else {
return nil
}
return sectionIndex
}
let insertedSectionsSet = IndexSet(insertedSections)
let insertedRows = insertedPlaylists.compactMap { tuple -> IndexPath? in
if let section = self.sectionIndexes.firstIndex(of: tuple.key) {
if let row = self.playlistsDictionary[tuple.key]?.firstIndex(where: { $0.equals(tuple.list) }) {
return IndexPath(row: row, section: section)
}
}
return nil
}
print("update")
update?(insertedSectionsSet, insertedRows)
}

RxSwift+Moya+Moya_ObjectMapper+MJRefresh, refresh failed?

I'm newer to RxSwift. I want to refresh the tableview to show new data.The first request that I can get the data. but when I pull down the tableview, the request didn't finished. I have no ideas about this? My code is belowing:
1: My viewController's code:
class RecommendViewController: UIViewController {
lazy var tableView = DefaultManager.createTableView(HomeImageCell.self,
HomeImageCell.idenfitier)
let disposeBag = DisposeBag()
lazy var viewModel = HomeViewModel()
lazy var dataSource: [HomeListDetailModel] = []
override func viewDidLoad() {
super.viewDidLoad()
viewModel.fetchRecommendList("answer_feed",0)
setupTableView()
configureRefresh()
bindDataToTableView()
}
func setupTableView() {
view.addSubview(tableView)
tableView.snp.makeConstraints { (make) in
make.edges.equalTo(0)
}
tableView.estimatedHeight(200)
}
func bindDataToTableView() {
viewModel.recommend
.observeOn(MainScheduler.instance)
.do(onNext: { [unowned self] model in
print("endAllRefresh")
self.endAllRefresh()
}, onError: { (error) in
self.endAllRefresh()
print("error = \(error)")
})
.map { [unowned self] model in
return self.handleData(model)
}.bind(to: tableView.rx.items(cellIdentifier: HomeImageCell.idenfitier , cellType: HomeImageCell.self )) { index, model, cell in
cell.updateCell(data: model)
}.disposed(by: disposeBag)
}
func configureRefresh() {
tableView.mj_header = MJRefreshNormalHeader(refreshingBlock: { [unowned self] in
let model = self.dataSource[0]
self.viewModel.fetchRecommendList("answer_feed",model.behot_time)
})
tableView.mj_footer = MJRefreshAutoNormalFooter(refreshingBlock: { [unowned self] in
let model = self.dataSource[self.dataSource.count - 1]
self.viewModel.fetchRecommendList("answer_feed",model.behot_time)
})
}
func endAllRefresh() {
self.tableView.mj_header.endRefreshing()
self.tableView.mj_footer.endRefreshing()
}
func handleData(_ model: HomeListModel) -> [HomeListDetailModel] {
guard let data = model.detailData else {
return dataSource
}
self.dataSource = data
return data
}
}
2: My ViewModel
protocol HomeProtocol {
func fetchRecommendList(_ category: String, _ behot_time: Int)
}
class HomeViewModel: HomeProtocol {
lazy var provider = HTTPServiceProvider.shared
var recommend: Observable<HomeListModel>!
init() {}
init(_ provider: RxMoyaProvider<MultiTarget>) {
self.provider = provider
}
func fetchRecommendList(_ category: String, _ behot_time: Int) {
recommend = provider.request(MultiTarget(HomeAPI.homeList(category: category,behot_time: behot_time)))
.debug()
.mapObject(HomeListModel.self)
}
}
When I made a breakpoint at request method, it didn't do a request? Does anyone know it ? Thanks first
SomeOne told me the reason,So I write it here. In my ViewModel recommend should be backed by PublishSubject or BehaviourSubject or ReplaySubject and then I should share this for View as Observable. In fetchRecommentList method I should bind request to created Subject.
Now I have created observable, but request will run after subsribe or bind

Resources