What is PassthroughSubject & CurrentValueSubject - ios

I happen to look into Apple's new Combine framework, where I see two things
PassthroughSubject<String, Failure>
CurrentValueSubject<String, Failure>
Can someone explain to me what is meaning & use of them?

I think we can make analogies with real world cases.
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.
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.

Both PassthroughSubject and CurrentValueSubject are publishers that conform to the Subject protocol which means you can call send on them to push new values downstream at will.
The main difference is that CurrentValueSubject has a sense of state (current value) and PassthroughSubject simply relays values directly to its subscribers without remembering the "current" value:
var current = CurrentValueSubject<Int, Never>(10)
var passthrough = PassthroughSubject<Int, Never>()
current.send(1)
passthrough.send(1)
current.sink(receiveValue: { print($0) })
passthrough.sink(receiveValue: { print($0) })
You'd see that the current.sink is called immediately with 1. The passthrough.sink is not called because it has no current value. The sink will only be called for values that are emitted after you subscribe.
Note that you can also get and set the current value of a CurrentValueSubject using its value property:
current.value // 1
current.value = 5 // equivalent to current.send(5)
This isn't possible for a passthrough subject.

PassthroughSubject is used for representing events. Use it for events like button tap.
CurrentValueSubject is used representing state. Use it for storing any value, say state of switch as off and on.
Note: #Published is kind of CurrentValueSubject.

PassthroughSubject and CurrentValueSubject are both Publishers — a type introduced by Combine — that you can subscribe to (performing operations on values when values are available).
They both are designed to make it easy to transfer to using the Combine paradigm. They both have a value and an error type, and you can "send" values to them (making the values available to all subscribers)
The main difference between the two that I've seen is that CurrentValueSubject starts with a value, while PassthroughSubject does not. PassthroughSubject seems easier to grasp conceptually, at least for me.
PassthroughSubject can easily be used in place of a delegate pattern, or to convert an existing delegate pattern to Combine.
//Replacing the delegate pattern
class MyType {
let publisher: PassthroughSubject<String, Never> = PassthroughSubject()
func doSomething() {
//do whatever this class does
//instead of this:
//self.delegate?.handleValue(value)
//do this:
publisher.send(value)
}
}
//Converting delegate pattern to Combine
class MyDel: SomeTypeDelegate {
let publisher: PassthroughSubject<String, Never> = PassthroughSubject()
func handle(_ value: String) {
publisher.send(value)
}
}
Both of these examples use String as the type of the value, while it could be anything.
Hope this helps!

PassthroughSubject is suitable for event like tap action
CurrentValueSubject is suitable for state

Already a lot of good answers posted in this thread, just thought of adding this answer for someone who is coming from using RxSwift.
PassThroughSubject is like PublishSubject where it broadcasts an event to its subscribers, probably with some value passed along.
CurrentValueSubject is similar to BehaviorRelay where a single value is persisted with the subject instance and passed along during the event broadcast.

Related

Mutable observable object in RxSwift or Combine

What I'm trying to achieve is to have a mutable Array in viewModel that can also be observed by Controller and reload tableView when its value changes.
I tried to use BehaviorRelay but looks like it's immutable. I can't delete and insert data inside it's value.
The best thing I managed to write is this custom ObservableObject class:
final class CustomObservableObject<T> {
var value: T {
didSet {
listener?(value)
}
}
init(value: T) {
self.value = value
}
var listener: ((T) -> Void)?
func bind(listener: #escaping ((T) -> Void)) {
self.listener = listener
}
}
which works exactly how I want. Is there anything I can use in the Combine or RxSwift framework like this class?
Thanks in advance
A BehaviorRelay is built for the specific purpose of storing state and allowing you to imperatively update it which is what you are asking for.
The BehaviorRelay type is a wrapper around the BehaviorSubject type and provides a different interface, one that does not allow the internal subject to emit an error or completed event. The Element contained in the BehaviorSubject is mutable. However, in order to mutate it you must first extract it into a var with value and then push the updated value back in with accept(_:). This will help ensure that no mutations are missed and allows you to batch up multiple mutations into a single update. These are things missing in the class you wrote.
Please always remember though:
Subjects [and Relays] provide a convenient way to poke around Rx, however they are not recommended for day to day use.
-- Introduction to Rx
As you learn more about, and grow more comfortable with, the reactive paradigm you will find yourself reaching for this particular crutch less often.

RxSwift Observing test cases on mocked UserDefaults

I am observing for value change in userDefaults
userDefaults.rx.observe(Int.self, option.userDefaultsKey)
For unit testing I did userDefaultsMock, I am not getting how to emit when value is updated or set for those particular keys
override func setValue(_ value: Any?, forKey key: String) {
tempArray[key] = value
}
An Observable chain starts with a side effect (such as the observe(_:_:) you posted) and ends with a side effect (whatever is in the subscribe.) Everything in-between the beginning of the chain and the end is logic [1]. Move that logic to a separate function so you can test it without the mock.
[1] A couple of exceptions to this rule are do() calls and flatMap calls. Everything else is logic that needs to be tested.
Actually I couldn't mock rx.observe but did some work around to accomplish.
I used UserDefaults but with suiteName so not disturbing standard database
So this trick helped me as observable got notified.
I got idea from https://medium.com/#davidlin_98861/testing-userdefaults-cd86849fd896

Swift bind AnyObserver to Observable

So, for example I have this observer in var myObserver: AnyObserver<Bool>
Can I somehow subscribe to it like for an observable, so I would be able to do some operations with it like .subscribe(onNext:) ?
An Observer is what you actually pass to subscribe(...)
In this example, you could do
var myObserver: AnyObserver<Bool> = //...
myObservable
.subscribe(myObserver)
.disposed(by: disposeBag)
The syntax subscribe(onNext:onError:onCompleted:) is just syntactic sugar to not have to actually create an Observer object every time.
Observable is a read-only entity. You can only subscribe to "Read" things off of it and manipulate/transform them.
The "Writable" portion of Rx is called an Observer and is usually abstracted with either a Subject, a Relay or a manual creation using Observable.create.
You can read more about Subjects here:
https://speakerdeck.com/freak4pc/rxswift-debunking-the-myth-of-hard?slide=34

Creating an observable with RxSwift

I'm using RxSwift and I'm trying to extend another library I'm using to make something observable.
The library basically calls a delegate method every time a value changes, and I want to hook into this and whenever it calls the delegate, also add the new value to an observable sequence I want to create in my subclass.
I've seen how you can create observable sequences, but in each example next events are sent to the observer within the block given to the Observable.create method. I have no idea how I can add things to the observable sequence from "outside" this block passed to create.
I just want to create something that I can observe or can drive things with RxSwift, and manually add to the sequence at certain points.
I'd be very grateful if someone could point me in the right direction, as I'm very new to this.
Observable is readonly interface. Sequences created by Observable.create can only produce the value(s) given at construction time and nothing more. You can't "add things" to it, to use your words. Speaking in RxSwift terms, you can't do away with just Observable interface, you need also ObserverType - it must also observe your mutating value. There is more than one way to do it in RxSwift, but i think that you need PublishSubject:
let value = PublishSubject<YourType>()
let disposer = DisposeBag()
init() {
value.subscribe(onNext: { (newValue) in
// use newValue ...
}).addDisposableTo(disposer)
}
func yourDelegateHandler(newValue: YourType)
{
value.onNext(newValue)
}

Understanding ReactiveCocoa and MVVM in my ReactiveCocoa test project

I've written a very simple ReactiveCocoa test application to try my hand at coding in RAC (rather than just reading about it endlessly). It's on Github, and I wanted to get some specific questions about it answered. I'll link to the code components as I go along.
First, a brief explanation of the application: it's a timer-driven iteration counter that can be paused by the user. (Its purpose is to count how many seconds have elapsed, eliding the ones where the user paused it.) Once a second, a timer increments a variable iff the user hasn't paused the incrementing behaviour.
There are three classes I'm concerned about hearing feedback for:
MPSTicker (.m), which performs "accumulate since initialization unless paused" and provides that result on a signal. It has a public BOOL property to control whether or not accumulation is running.
MPSViewModel (.m), which provides a ViewModel wrapping from MPSTicker to the view controller. It provides read-only strings which show the number of ticks and show the text for the action which, if taken, "pauses" or "resumes" ticks. It also has a read-write BOOL for pausing/unpausing ticks.
MPSViewController (.m), which consumes strings from MPSViewModel by binding a label to the ViewModel's tick string, binding a button's text to the "tick action" string, and mapping a button's press into the ViewModel's paused property.
My questions:
I don't like the BOOL property on MPSTicker for enabling/disabling its accumulation, but I didn't know how to do it more Reactive-ly. (This also runs downstream to the ViewModel and ViewController: how can I run a string through all three of these to control whether or not the ticker is running?)
The ViewModel exposes tickString and tickStateString as very traditional properties, but the ViewController which consumes these immediately maps them back into text on a label and button text with RACObserve. This feels wrong, but I don't know how to expose a signal from the ViewModel that's easy for the ViewController to consume for these two attributes.
The ViewController suffers an indignity when flipping the paused BOOL on the ViewModel. I think this is another downstream effect of #1, "This shouldn't be a BOOL property", but I'm not sure
(Notes: I think I shied away from a signal for the BOOL of paused on MPSTicker because I didn't know how to consume it in the ViewModel to derive two strings (one for the current tick count, and one for the action text), nor how to push UI-driven value changes when the user pushes the "pause" or "resume" button. This is my core concern in questions 1 and 3.)
Some screenshots to help you visualize this gorgeous design:
Ticking:
Paused:
This is such an awesome write-up!
I don't like the BOOL property on MPSTicker for enabling/disabling its accumulation, but I didn't know how to do it more Reactive-ly. (This also runs downstream to the ViewModel and ViewController: how can I run a string through all three of these to control whether or not the ticker is running?)
Broadly, there's nothing wrong or non-Reactive about using properties. KVO-able properties can be thought of as behaviors in the academic FRP sense: they're signals which have a value at all points in their lifetime. In fact, in Objective-C properties can be even better than signals because they preserve type information that we'd otherwise lose by wrapping it in a RACSignal.
So there's nothing wrong with using KVO-able properties if it's the right tool for the job. Just tilt your head, squint a bit, and they look like signals.
Whether something should be a property or RACSignal is more about the semantics you're trying to capture. Do you need the properties (ha!) of a property, or do you care more about the general idea of a value changing over time?
In the specific case of MPSTicker, I'd argue the transitions of accumulateEnabled are really the thing you care about.
So if MPSTicker had a accumulationEnabledSignal property, we'd do something like:
_accumulateSignal = [[[[RACSignal
combineLatest:#[ _tickSignal, self.accumulationEnabledSignal ]]
filter:^(RACTuple *t) {
NSNumber *enabled = t[1];
return enabled.boolValue;
}]
reduceEach:^(NSNumber *tick, NSNumber *enabled) {
return tick;
}]
scanWithStart:#(0) reduce:^id(NSNumber *previous, id next) {
// On each tick, we add one to the previous value of the accumulate signal.
return #(previous.unsignedIntegerValue + 1);
}];
We're combining both the tick and the enabledness, since it's the transitions of both that drive our logic.
(FWIW, RACCommand is similar and uses an enabled signal: https://github.com/ReactiveCocoa/ReactiveCocoa/blob/9503c6ef7f2f327f4db6440ddfbc4ee09b86857f/ReactiveCocoaFramework/ReactiveCocoa/RACCommand.h#L95.)
The ViewModel exposes tickString and tickStateString as very traditional properties, but the ViewController which consumes these immediately maps them back into text on a label and button text with RACObserve. This feels wrong, but I don't know how to expose a signal from the ViewModel that's easy for the ViewController to consume for these two attributes.
I may be missing your point here, but I think what you've described is fine. This goes back to the above point about the relationship between properties and signals.
With RAC and MVVM, a lot of the code is simply threading data through to other parts of the app, transforming it as needed in its particular context. It's about the flow of data through the app. It's boring—almost mechanical—but that's kinda the point. The less we have to re-invent or handle in an ah hoc way, the better.
FWIW, I'd change the implementation slightly:
RAC(self, tickString) = [[[[_ticker
accumulateSignal]
deliverOn:[RACScheduler mainThreadScheduler]]
// Start with 0.
startWith:#(0)]
map:^(NSNumber *tick) {
// Unpack the value and format our string for the UI.
NSUInteger count = tick.unsignedIntegerValue;
return [NSString stringWithFormat:#"%i tick%# since launch", count, (count != 1 ? #"s" : #"")];
}];
That way we're more explicitly defining the relationship of tickString to some transformation of ticker (and we can avoid doing the strong/weak self dance).
The ViewController suffers an indignity when flipping the paused BOOL on the ViewModel. I think this is another downstream effect of #1, "This shouldn't be a BOOL property", but I'm not sure
I'm probably just missing it due to tiredness, but what's the indignity you have in mind here?

Resources