What is the difference between PublishSubject and BehaviorRelay
I think it would make sense to first define the difference between PublishSubject and BehaviorSubject. Both are observers and subscribers at the same time, meaning they can receive events and be subscribed to. Where they differ is:
PublishSubject
Starts empty
Only emits new values to subscribers
BehaviorSubject
Needs a starting value
Replays the last value to new subscribers
Once we get this out of the way, the BehaviorRelay is easy and the doc says:
BehaviorRelay is a wrapper for BehaviorSubject.
Unlike BehaviorSubject it can't terminate with error or completed.
In other words, Relay won't terminate.
Reference: http://reactivex.io/documentation/subject.html
Related
How can a buffered replay subject be implemented in ReactiveSwift?
I've looked at replayLazily(upTo:) operator of SignalProducer, and also the pipe() function of the Signal type, however I can't see a straightforward way of creating something equivalent to Rx ReplaySubject.
This brings up the following questions as well:
ReactiveSwift implements Subject with Signal.pipe(), however you can't specify a buffer for the pipe the same way you can for a Rx ReplaySubject. Are there any workarounds?
replayLazily(upTo:) operator is missing from the Signal type. I guess this is not so bad since you can create a SignalProducer from a Signal. But why does Signal not have the same operator?
Has anyone encountered this problem before? Or am I missing something?
Any help would be much appreciated.
The Signal docs say:
An observer of a Signal will see the exact same sequence of events as all other observers. In other words, events will be sent to all observers at the same time.
This is in contrast to producers, which create a new signal each time they are started, which means it's possible for each observer to see different events.
The buffering scenario requires each observer to receive a list of current values in the buffer upon subscription, and other observers should not receive these values each time a new observer is added. Therefore, each observer needs their own signal, and that means the buffering mechanism must be implemented as a producer which can create a new signal for each subscriber.
There's a good discussion from 2016 when replayLazily was added that hopefully clarifies the thinking behind the operator and why it absolutely can't be a part of Signal.
I'm quite new in RxSwift world and apparently I'm not using it correctly... I have a button that I would like to connect to an observer like this
button.rx.tap.bind(to: viewModel.someObserver).disposed(by: disposeBag)
where someObserver in viewModel is initialized as follows:
let publishSubject = PublishSubject<Void>()
someObserver = publishSubject.asObserver()
someObservable = publishSubject.asObservable()
However, when Disposable created with binding is disposed, PublishSubject which is used both as Observer and Observable gets invalidated and all new subscriptions are immediately disposed.
I would like to use my PublishSubject for a longer time and be able to subscribe to it after the binding is disposed. How to achieve that?
Have a look at PublishRelay, which can't terminate with an error or completed event.
Binding the taps to a PublishRelay will simply ignore the completed event (source) once the subscription is disposed, e.g. when the button is deallocated. This will allow you to subscribe to the PublishRelay at a later point
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
In my class, there are some values that may be observed by other part of the app. These values is can be read and written to database.
I have a collection that retains some RACSubjects.
When an object need to observe a value V1, I'll create a RACSignal for it, name it S1, and later if any other object is also interested in V1, I'll give it V1 too, so that when S1 changed, I can call [RACSubject sendNext:] to notify objects that is interested in it.
But there is a problem, I don't know when to release S1, since I don't know how to get notified when there is no object subscribing to S1.
Is there any way to do this?
I'm mentally rewording part of your question to the following (my changes italicized), because I think the original phrasing had some typos:
When an object need to observe a value V1, I'll create a RACSubject for it, name it S1, and later if any other object is also interested in V1, I'll give it S1 too, so that when V1 changed, I can call [RACSubject sendNext:] to notify objects that is interested in it.
If this is an incorrect interpretation, ignore this answer.
If you aren't explicitly retaining a signal yourself, ReactiveCocoa will automatically reclaim it when it runs out of subscribers. The relevant excerpt:
A created signal is automatically added to a global set of active signals.
The signal will wait for a single pass of the main run loop, and then remove itself from the active set if it has no subscribers. Unless the signal was retained somehow, it would deallocate at this point.
If something did subscribe in that run loop iteration, the signal stays in the set.
Later, when all the subscribers are gone, step 2 is triggered again.
But there's one problem: this doesn't apply to RACSubjects. They aren't added to the global set of active signals.
However, there's a... workaround you can apply in order to get this nice auto-retain behavior.
RACSignal *autoretainedSignal = [subject map:^(id x) { return x; }]
As long as you only subscribe to the autoretainedSignal, not the underlying RACSubject, you can take advantage of the auto-retain behavior that normal signals get.
So how do you hold onto it without retaining it? If you only have one signal that you're interesting in caching/sharing, you can just store it in a weak property. If there are multiple properties that you're dynamically managing, an NSMapTable with weak storage is your friend.
You'll need to hold onto both the underlying subject (so you can send events on it) and the derived signal (so you can provide it to subscribers) weakly. As long as the derived signal has subscribers, it will keep its underlying subject alive, and as soon as it runs out of subscribers both it and its subject will be deallocated.
I found an idea for you :)
#property (nonatomic,assign) NSInteger countOfSubscribers = 0;
later in your code
RACSignal *s; // here is your target signal
[[s rac_signalForSelector:#selector(subscribeNext:)]
subscribeNext:^(id x) {
self.countOfSubscribers++;
}];
[[[s rac_signalForSelector:#selector(subscribeNext:)] rac_willDeallocSignal]
subscribeNext:^(id x) {
self.countOfSubscribers--;
}];
Main idea is to subscribe to 'subscribeNext:' method, and when original subscriber will be removed - rac_willDeallocSignal
will called.
It's not completely tested snippet, but i hope this can help you to find right direction.
I have one signal that basically what it does is requesting for a configuration using NSRULSession. When I do a subscribeNext it does the request perfectly fine, however for the second time this request is not necessary anymore. How could I avoid it?
Your signal will do its work each time it is subscribed to unless you do something explicit to prevent that. It sounds like what you want here is the replayLast operator. This operator will cache the last emitted value of your signal and emit it when your signal is subscribed to again instead of redoing the initial work.
Read up on the 'replay' operators here:
http://spin.atomicobject.com/2014/06/29/replay-replaylast-replaylazily/
One time signal could be made via take: operator. You just need to pass an argument for the amount of times required to perform a signal. After such amount of executions this gateway will be closed completely and no more data will be passed in the subscribeNext: block. In your case this amount would be equal 1.
RACSignal *requestConfigurationSignal = ...
[[requestSignal
take:1]
subscribeNext:^(id value){
NSLog(#"Request in progress")
}]
Use a property and an action whose values are bound to that property. Then trigger the action only as needed to refresh the property's value.