Как мне наблюдать сигнал и сразу же получать событие "следующего", если оно уже произошло?
Я пытаюсь обернуть вызов API, который инициализирует объект после сетевого запроса. Я не хочу, чтобы сетевой запрос происходил для каждого нового наблюдателя, поэтому, насколько я понимаю, я не должен использовать SignalProducer
, Тем не менее, с помощью одного Signal
только при первом его использовании next
событие, в то время как новые подписчики никогда не получат текущее значение. Как я должен это делать? Я, вероятно, делаю что-то в корне неправильно с RAC.
extension SparkDevice {
static func createMainDeviceSignal() -> Signal<SparkDevice, NSError> {
return Signal {
sink in
SparkCloud.sharedInstance().getDevices { (sparkDevices: [AnyObject]!, error: NSError!) -> Void in
if let error = error {
sink.sendFailed(error)
}
else {
if let devices = sparkDevices as? [SparkDevice] {
if devices.count > 0 {
sink.sendNext(devices[0])
}
}
}
}
return nil
}
}
}
class DeviceAccess {
let deviceSignal: Signal<SparkDevice, NSError>
init() {
self.deviceSignal = SparkDevice.createMainDeviceSignal()
}
}
Я подумал об использовании MutableProperty
, но это, кажется, требует свойства по умолчанию, которое, кажется, не имеет смысла для этого.
Как я должен на самом деле идти об этом?
1 ответ
Что вам нужно, так это многоадресная рассылка. Тем не мение, ReactiveCocoa
3/4 не предлагает простой способ сделать это (в отличие от Rx), потому что они часто приводят к тонне сложности.
Иногда это действительно необходимо, как в вашем примере, и это может быть легко реализовано с помощью PropertyType
,
Я бы начал с создания холодного сигнала, который делает запрос. Это должно быть SignalProducer
:
private func createMainDeviceSignalProducer() -> SignalProducer<SparkDevice, NSError> {
return SignalProducer { observer, _ in
....
}
}
Если вы выставите это как есть, побочные эффекты будут происходить каждый раз, когда этот продюсер start
редактор к multicast
эти значения вы можете обернуть его в свойстве и выставить property
"s producer
вместо:
public final class DeviceAccess {
public let deviceProducer: SignalProducer<SparkDevice, NoError>
private let deviceProperty: AnyProperty<SparkDevice?>
init() {
self.deviceProperty = AnyProperty(
initialValue: nil, // we can use `nil` to mean "value isn't ready yet"
producer: SparkDevice.createMainDeviceSignal()
.map(Optional.init) // we need to wrap values into `Optional` because that's the type of the property
.flatMapError { error in
fatalError("Error... \(error)") // you'd probably want better error handling
return .empty // ignoring errors, but you probably want something better.
}
)
self.deviceProducer = deviceProperty
.producer // get the property producer
.ignoreNil() // ignore the initial value
}
}
Сейчас DeviceAccess.deviceProducer
будет воспроизводить значения, испускаемые основным производителем, а не повторять побочные эффекты. Обратите внимание, что это не ленивый: основной SignalProducer
будет начато сразу