Subjects
Subject
CurrentValueSubject
Holds the current value of the type defined.
CurrentValueSubject
is similar to BehaviorRelay
where a single value is persisted with the subject instance and passed along during the event broadcast.
CurrentValueSubject
is suitable for state
Below analogy by a fellow Stack overflow user - link in reference.
**CurrentValueSubject = A light switch** Someone turns on the lights in your home when you are outside. You get back home and you know someone has turned them on.
CurrentValueSubject has an initial state, it retains the data you put in as its state.
Initialization
private let subject: CurrentValueSubject<String, Never>
var output: String = ""
Sending values
subject.value = "Kautilya"
subject.value = "Sensehack"
subject.send(completion: .finished)
Subscription
You can subscribe to the subject for its values and listen to them in a stream.
subject
.dropFirst()
.map { String("Hello \($0)" }
.assign(to: &output)
The reason we start the pipeline by calling dropFirst
is because a CurrentValueSubject
emits its current value when a subscription is attached to it. Since that’ll always be an empty subject
string in this case, we’re simply ignoring it.
Quirks
UIKit
Well suited for UIKit + Combine.
didSet
internally it calls so that UIKit or subscribers can make the necessary changes with CurrentValueSubject
value and it behaves differently compared to @Publisher
. publisher
Initialization Optionals
If we just define the current value subject with optional then it won't subscribe.
Wrong Way
var users: CurrentValueSubject<UsersConsumableViewModel?, Error>
Right Way
var users = CurrentValueSubject<UsersConsumableViewModel?, Error>(nil)
It needs to be initialized with something in order for its subscribers to listen to its state. Another 18 mins spent around debugging and understanding why my data doesn't appear on the screen.
I love reactive paradigm and chasing its bugs asynchronously.
Skip First event
Operator prefix
$subject
.dropFirst()
.sink { }
.store(&cancellable)
operatorsPartitioners/ operatorsprefix
ignore-first-number-of-elements-from-a-publisher
PassthroughValueSubject
Holds the no initial value of the type defined.
PassThroughSubject
is like PublishSubject
PassthroughSubject
is suitable for event like tap action
Analogy by a fellow Stack overflow user - link in reference.
**PassthroughSubject = A doorbell push button**
When someone rings the door, you are notified only if you are at home (you are the subscriber)
PassthroughSubject doesn't have a state, it emits whatever it receives to its subscribers.
Initialization
var passthrough = PassthroughSubject<Int, Never>()
var numberString: String = ""
Sending values
Can't use passthrough.value = 5
passthrough.send(1)
passthrough.send(2)
Subscription
You can subscribe to the subject for its values and listen to them in a stream.
The sink will only be called for values that are emitted after you subscribe.
passthrough
.map { String("Number \($0)" }
.sink(receiveValue: { print($0) })
.store(in: &anyCancellables)
Binding
Error Warning
SwiftUI: Cannot convert value of type 'Bool' to expected argument type 'Binding Bool'
nameOfBinding.wrappedValue
If there is a requirement of binding variable to be provided not the actual value you can pass $
to notify its a bindable property type.
Toggle(isOn: $character.isSelected){
Input | Output
extension CustomViewModel: ViewModelType {
  struct Input {
  var startSession: PassthroughSubject<Void, Never>
var spanEnded: PassthroughSubject<String, Never>
var sessionEnded: PassthroughSubject<String, Never>
  }
 Â
  struct Output: ViewStateDrivable {
  let viewState: AnyPublisher<String, Never>
  }
}
protocol ViewModelType {
associatedtype Input
associatedtype Output
var input: Input { get }
var output: Output { get }
}
Reference
avanderlee | passthroughsubject currentvaluesubject explained