Может ли наблюдатель сигнала получить доступ к последнему излученному значению сигнала ReactiveCocoa?

Я начинаю использовать ReactiveCocoa и все еще борюсь с некоторыми основными понятиями:

  1. Мое приложение начинает прослушивать данные геолокации (init на мой взгляд модель)
  2. Мое приложение выдает сигнал с моим текущим местоположением (didFindCurrentPosition называется)
  3. Контроллер моего вида, показывающий загрузку карты (viewDidLoad на мой взгляд контроллер)
  4. Контроллер моего вида начинает наблюдать сигнал текущего местоположения (все еще viewDidLoad)

Моя проблема: после выполнения шага 2, если никакое другое событие не отправлено по сигналу, мой контроллер представления не получает уведомление.

Как мой контроллер просмотра может получить доступ к последнему значению из сигнала? (т.е. как получить доступ на шаге 3 к значению, испускаемому на шаге 2?)

Спасибо за вашу помощь.

PS: ReactiveCocoa выглядит как отличная библиотека, но я озадачен состоянием документации. ИМХО, это не очень понятно и не хватает четких руководств по его использованию.

Код

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

class MyViewModel: LocationManagerDelegate {
    let locationManager: LocationManager
    let geolocationDataProperty = MutableProperty<Geolocation?>(nil)
    let geolocationData: Signal<Geolocation?, NoError>

    init() {
        geolocationData = geolocationDataProperty.signal

        // Location Management
        locationManager = LocationManager()
        locationManager.delegate = self
        locationManager.requestLocation()
    }

    // MARK: - LocationManagerDelegate

    func didFindCurrentPosition(location: CLLocation) {
        geolocationDataProperty.value = Geolocation(location: location)
    }
}

Контроллер вида:

class MyViewController: UIViewController {
    let viewModel = MyViewModel()

    init() {
        super.init(nibName: nil, bundle: nil)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        viewModel.geolocationData
            .observe(on: UIScheduler())
            .observeValues { geolocation in
                debugPrint("GOT GEOLOCATION")
        }
    }
}

3 ответа

Решение

У вас уже есть Property который содержит последнее значение, выбрасываемое. Вместо использования свойства signal использовать producer, Таким образом, когда вы начинаете producer сначала вы получите текущее значение (ни один не был отправлен, вы получите ноль).

Ср документация:

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

Итак, что касается кода в вопросе, viewDidLoad Метод должен сделать что-то вроде следующего:

viewModel.geolocationDataProperty
        .producer
        .start(on: UIScheduler())
        .startWithValues { geolocation in
            debugPrint("GOT GEOLOCATION")
    }

Привязки из любого вида потоков значений можно создать с помощью оператора <~, но вы можете начать изменять свой код следующим образом и убедиться, что он работает нормально, его легче отлаживать:-)

class MyViewModel: LocationManagerDelegate {
    let locationManager: LocationManager
    let geolocationDataProperty = MutableProperty<Geolocation?>(nil)

    init() {
        geolocationDataProperty.signal.observeValues { value in
            //use the value for updating the UI
        }

        // Location Management
        locationManager = LocationManager()
        locationManager.delegate = self
        locationManager.requestLocation()
    }

    // MARK: - LocationManagerDelegate

    func didFindCurrentPosition(location: CLLocation) {
        geolocationDataProperty.value = Geolocation(location: location)
    }
}

тогда мы можем попытаться использовать бинарный оператор для реализации шаблона MVVM, поскольку иметь прямую ссылку на элемент пользовательского интерфейса внутри представления модели, безусловно, плохая идея:-)

Посмотрите на эту статью, это действительно интересно.

Я не уверен, поддерживает ли ваш компонент карты реактивную структуру.

увидеть, что он имеет расширение ".reactive" и может ли он использоваться для обновления свойства текущей позиции

если он присутствует, оператор <~ может использоваться для записи чего-то похожего на

map.reactive.position <~ viewModel.geolocationDataProperty

если у него нет реактивного расширения, вы можете просто переместить этот код в ваш viewcontroller

viewModel.geolocationDataProperty.signal.observeValues { value in
                //use the value for updating the UI
            }

Если ваша проблема заключается в том, чтобы иметь старое значение, когда оно будет изменено. Вы можете попробовать с наблюдением значения ключа. Добавьте вашу собственность для наблюдения за ценностями, например

 geolocationDataProperty.addObserver(self, forKeyPath: "value", options: [.new, .old], context: &observerContext)

Всякий раз, когда ваш geolocationDataProperty изменяется, вы получите это значение в методе делегата

 override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        let newValue = change?[.newKey] as? NSObject
         let oldValue = change?[.oldKey] as? NSObject
//Add your code
    }
Другие вопросы по тегам