Как мне наблюдать сигнал и сразу же получать событие "следующего", если оно уже произошло?

Я пытаюсь обернуть вызов 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 будет начато сразу

Другие вопросы по тегам