The owner of a Signal
has unilateral control of the event stream. Observers may register their interests in the future events at any time, but the observation would have no side effect on the stream or its owner.
It is like a live TV feed — you can observe and react to the content, but you cannot have a side effect on the live feed or the TV station.
let channel: Signal<Program, NoError> = tvStation.channelOne
channel.observeValues { program in ... }
See also: The Signal
overview, The Signal
contract, The Signal
API reference
A Signal
may have any arbitrary number of events carrying a value, following by an eventual terminal event of a specific reason.
It is like a frame in a one-time live feed — seas of data frames carry the visual and audio data, but the feed would eventually be terminated with a special frame to indicate "end of stream".
See also: The Event
overview, The Signal
contract, The Event
API reference
SignalProducer
defers work — of which the output is represented as a stream of values — until it is started. For every invocation to start the SignalProducer
, a new Signal
is created and the deferred work is subsequently invoked.
It is like a on-demand streaming service — even though the episode is streamed like a live TV feed, you can choose what you watch, when to start watching and when to interrupt it.
let frames: SignalProducer<VideoFrame, ConnectionError> = vidStreamer.streamAsset(id: tvShowId)
let interrupter = frames.start { frame in ... }
interrupter.dispose()
See also: The SignalProducer
overview, The SignalProducer
contract, The SignalProducer
API reference
Property
is a variable that can be observed for its changes. In other words, it is a stream of values with a stronger guarantee than Signal
— the latest value is always available, and the stream would never fail.
It is like the continuously updated current time offset of a video playback — the playback is always at a certain time offset at any time, and it would be updated by the playback logic as the playback continues.
let currentTime: Property<TimeInterval> = video.currentTime
print("Current time offset: \(currentTime.value)")
currentTime.signal.observeValues { timeBar.timeLabel.text = "\($0)" }
See also: The Property
overview, The Property
contract, The property API reference
When being invoked with an input, Action
apply the input and the latest state to the preset action, and pushes the output to any interested parties.
It is like an automatic vending machine — after choosing an option with coins inserted, the machine would process the order and eventually output your wanted snack. Notice that the entire process is mutually exclusive — you cannot have the machine to serve two customers concurrently.
// Purchase from the vending machine with a specific option.
vendingMachine.purchase
.apply(snackId)
.startWithResult { result
switch result {
case let .success(snack):
print("Snack: \(snack)")
case let .failure(error):
// Out of stock? Insufficient fund?
print("Transaction aborted: \(error)")
}
}
// The vending machine.
class VendingMachine {
let purchase: Action<Int, Snack, VendingMachineError>
let coins: MutableProperty<Int>
// The vending machine is connected with a sales recorder.
init(_ salesRecorder: SalesRecorder) {
coins = MutableProperty(0)
purchase = Action(state: coins, enabledIf: { $0 > 0 }) { coins, snackId in
return SignalProducer { observer, _ in
// The sales magic happens here.
// Fetch a snack based on its id
}
}
// The sales recorders are notified for any successful sales.
purchase.values.observeValues(salesRecorder.record)
}
}
See also: The Action
overview, The Action
API reference
When observing a Signal
or SignalProducer
, it doesn't make sense to continue emitting values if there's no longer anyone observing them.
Consider the video stream: once you stop watching the video, the stream can be automatically closed by providing a Lifetime
:
class VideoPlayer {
private let (lifetime, token) = Lifetime.make()
func play() {
let frames: SignalProducer<VideoFrame, ConnectionError> = ...
frames.take(during: lifetime).start { frame in ... }
}
}
See also: The Lifetime
overview, The Lifetime
API reference