ReactiveSwift Обновить данные - PullRequest
       20

ReactiveSwift Обновить данные

0 голосов
/ 11 сентября 2018

Я начинающий в ReactiveSwift. Это выборка кода в моей модели просмотра:

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

Когда viewDidLoad вызывается в ViewController, тогда модель представления начинает извлекать данные. Как сообщить VC, что fetch является концом и что можно вызвать refreshData? Есть ли возможность перехватить конец функции viewDidLoad, я имею в виду после извлечения.

initCode:

init(weatherFetcher: WeatherFetcher) {
    self.weatherFetcher = weatherFetcher
    didStartLoadingWeather = self.viewDidLoadProperty.signal.skipNil()
}

Ответы [ 2 ]

0 голосов
/ 12 сентября 2018

Хорошей практикой для загрузки табличных представлений с использованием ReactiveSwift (если это ваш случай) является простое связывание данных табличного представления с MutableProperty <[ваши данные]>

, вы просто извлекаете свои данные и когдазначение получено, перезагрузите табличное представление.После этого представление таблицы будет волшебно обновлено.


в вашей модели представления:

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

в контроллере представления:

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
}

Если это не представление таблицы,просто придерживайся идеи и делай это на чем хочешь.Например, если это простая ячейка, загрузите ячейку с новыми данными:

в модели представления:

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

в контроллере представления:

    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
    }
0 голосов
/ 12 сентября 2018

Прежде всего, я бы посоветовал вам использовать ViewModel, которая будет отвечать за выполнение этих операций от имени UIViewController.

Отвечая на ваш вопрос напрямую. Вам придется использовать какой-то механизм для хранения данных. Это может быть Property или MutableProperty. Мой совет для первых. Вам также понадобится триггер, поэтому, когда произойдет viewDidLoad, вы можете сообщить об этом. Предполагая, что у вас есть 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: ())
    }
}

Я оставлю завершение fetch для вас. Но:

  1. Этот подход позволяет вам сохранять состояние вокруг (через свойство) и допускает внешний триггер.
  2. Теперь вы читаете значения из state.
  3. fetcher создается во время инициализации и запускается только после вызова fetch функции.
...