【问题标题】:Can a signal observer get access to the last emitted value of a ReactiveCocoa signal?信号观察者可以访问 ReactiveCocoa 信号的最后发出的值吗?
【发布时间】:2017-06-28 22:09:38
【问题描述】:

我开始使用 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")
        }
    }
}

【问题讨论】:

  • 我猜您正在使用 SignalProducer(冷信号)来获取地理位置数据。您是否使用 MutableProperty 来实现数据绑定?
  • @cristallo:我没有使用响应式概念来获取地理位置数据,只是常规的 CoreLocation 委托回调。我正在使用 MutableProperty 来存储位置,并从中获取信号。我在我的问题中添加了代码以使事情更清楚。

标签: ios swift reactive-programming reactive-cocoa reactive-cocoa-5


【解决方案1】:

您已经有一个Property,其中包含最新发出的值。不要使用属性的signal,而是使用producer。这样,当您启动 producer 时,您将首先获得当前值(如果没有发送,您将获得 nil)。

参照。 the documentation:

可以从 value getter 中获取属性的当前值。生产者 getter 返回一个信号生产者将发送属性的当前值,然后是随时间发生的所有变化。信号获取器返回一个信号,该信号将随时间发送所有变化,但不是初始值

因此,关于问题中的代码,viewDidLoad 方法应该执行以下操作:

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

【讨论】:

  • 谢谢。就是这样。我将通过引用此 + 代码的文档的链接更新您的答案。
【解决方案2】:

可以使用

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 模式,因为直接引用模型视图中的 UI 元素绝对是个坏主意:-)

看看这个article,真的很有趣。

我不确定您的地图组件是否直接支持响应式框架。

查看它是否具有“.reactive”扩展名以及是否可以用于更新当前位置属性

如果存在,则可以使用 的内容

map.reactive.position <~ viewModel.geolocationDataProperty

如果它没有响应式扩展,那么您可以简单地将这段代码移动到您的视图控制器中

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

【讨论】:

  • 这几乎就是我已经在做的事情(参见更新的代码)。我的问题是在调用 viewDidLoad 之前找到了地理位置。在调用 viewDidLoad 之后,观察者永远不会被触发,因为没有发生更新,我找不到获取信号看到的最新值的方法(即在调用 viewDidLoad 之前)
  • 每次调用 didFindCurrentPosition 时都应该触发该信号。在应用执行期间是否多次调用 didFindCurrentPosition 方法来更新您的位置?
  • 我对这个分配“geolocationData = geolocationDataProperty.signal”感到很困惑。它不应该是必需的,因为可变属性有自己的信号系统
  • 它的灵感来自 Kickstarter 的 MVVM 实现。它有助于隐藏视图模型的实现细节,并仅公开信号输出。
  • 在我的测试中,didFindCurrentPosition 总是被调用一次(在 viewDidLoad 被调用之前),之后再也不被调用。在调试过程中,我使用虚拟值明确调用它(在 viewDidLoad 之后),以检查我的绑定是否正常并且工作正常(= 调用了观察者)
【解决方案3】:

如果您的问题是在修改时具有旧值。您可以尝试使用 Key-Value Observing 。添加您的属性以进行价值观察,例如

 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
    }

【讨论】:

  • 谢谢艾伦。但是使用 KVO 在某种程度上违背了投资 ReactiveCocoa 的目的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-14
  • 1970-01-01
  • 1970-01-01
  • 2013-01-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多