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:
state
. 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
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.