Subscribe cancels the subject when the source publisher completes - swift

I have a function which receives a Publisher and creates a PassthroughSubject that is used to both:
subscribe to the source publisher
send values manually
E.g.:
class Adapter<T>{
let innerSubject=PassThroughSubject<T,Never>()
let scope = Scope() // custom type, Array<AnyCancellable>
func init(_ source:Publisher<T,Never>){
source.register(innerSubject).in(scope)
innerSubject.sink(receiveValue:{debugPrint($0)}).in(scope)
}
func adapt(_ T val){
innerSubject.send(val)
}
}
fn usage(){
let adapter=Adapter(Empty()) // change to Empty(completeImmediately:false) to fix
adapter.adapt(42) // should print 42,
}
The inner subject seems to be cancelled because Empty calls complete.
I would expect the inner subject's subscription to be cancelled, not the subject itself. In .Net at least that's the behaviour - am I missing something? Do I need to wrap this in a broadcasting subject? I thought that Publishers could receive multiple Receivers and multicast to each?

I would expect the inner subject's subscription to be cancelled, not the subject itself. In .Net at least that's the behaviour - am I missing something?
Just like with Observables, when a Publisher emits a completed event, it will not emit any more events. A Subject is a kind of Publisher. The behavior is exactly the same as in .Net so I can only assume you are missing something.
I thought that Publishers could receive multiple Receivers and multicast to each?
Yes they can. But they send the exact same values to each receiver, including the completion event. However, again just like with Observables and Rx Subjects, once it emits a completion event, it cannot emit anything else.

Related

RxSwift subscribe sequence

I have two subscribers for a BehaviorRelay observable type named profileUpdates.
Publishing my data through,
Observables.shared.profileUpdates.accept(data)
Subscribing in two points in code (Suppose A and B) through,
Observables.shared.profileUpdates.subscribe(onNext: { } )
Now, can I define the sequence I would be able to get the subscribed data or it is strictly dependant on the library?
For example, in point A after point B, or vice versa.
There is no documented contract that guarantees the order that subscribes will be called in. They will be called sequentially, but the order is undefined.
It would be best to use the do operator for this:
profileUpdates
.do(onNext: { value in
// perform side effect
})
.subscribe(onNext: { value in
// perform other side effect
})
.disposed(by: disposeBag)
However, excessive use of the do operator (and Relays for that matter) are a code smell and imply you are still thinking imperatively.

PassthroughSubject with no initial value

I want to create a Swift Combine publisher that transmits a value and always gives the latest value when someone subscribes to it. However I want to only transmit a value once I have one - from an asynchronous call that’s triggered by the first subscriber. Pass through subject doesn’t work for that need, is there any good way to accomplish this?
You can use CurrentValueSubject for this:
let subject = CurrentValueSubject<T, E>(someValue)
func whatever() -> AnyPublisher<T, E> {
return subject.dropFirst().eraseToAnyPublisher()
}

In Swift Combine, is the "root" object always a Subject?

In Apple's WWDC videos on Swift Combine, they always use NSNotificationCenter as the publisher of messages. However, a Publisher does not appear to have any ability to actually send a message on demand. That functionality appears to be in Subject.
Am I correct in assuming that a Subject must therefor be the root object of any chain of Publishers? Apple provides two built-in subjects called: CurrentValueSubject and PassthroughSubject.
But I assume I can write my own Subject using the appropriate protocols?
In Swift Combine, Publishers are a protocol describing an object which can transmit values over time.
A Subject is an extended publisher which knows how to send imperatively.
Neither Publisher nor Subject are concrete classes with implementation; they are both protocols.
Take a look at the Publisher protocol (and remember that a Subject is an extended Publisher):
public protocol Publisher {
associatedtype Output
associatedtype Failure : Error
func receive<S>(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input
}
To build a custom publisher you need only implement the receive function (and provide type information), in which you are given access to a subscriber. How would you send data to that subscriber from WITHIN the publisher?
For this we look at the Subscriber protocol to see what's available:
public protocol Subscriber : CustomCombineIdentifierConvertible {
...
/// Tells the subscriber that the publisher has produced an element.
///
/// - Parameter input: The published element.
/// - Returns: A `Demand` instance indicating how many more elements the subcriber expects to receive.
func receive(_ input: Self.Input) -> Subscribers.Demand
}
As long as you've saved a reference to any/all subscribers which have connected, your publisher can easily send changes into the pipeline by calling receive on the subscriber. However, you'll have to manage subscribers and diff changes on your own.
A Subject behaves the same but instead of streaming changes into the pipeline, it simply provides a send function for someone else to call. The two concrete Subjects that Swift provides have additional features like storage.
TL;DR changes aren't sent to publishers they're sent to subscribers. Subjects are publishers that can accept some input.

RxJava Relay vs Subjects

I'm trying to understand the purpose of this library by Jake Warthon:
https://github.com/JakeWharton/RxRelay
Basically: A Subject except without the ability to call onComplete or
onError. Subjects are stateful in a damaging way: when they receive an
onComplete or onError they no longer become usable for moving data.
I get idea, it's a valid use case, but the above seems easy to achieve just using the existing subjects.
1. Don't forward errors/completions events to the subject:
`observable.subscribe({ subject.onNext(it) }, { log error / throw exception },{ ... })`
2. Don't expose the subject, make your method signature return an observable instead.
fun(): Observable<> { return subject }
I'm obviously missing something here and I'm very curios on what it is!
class MyPublishRelay<I> : Consumer<I> {
private val subject: Subject<I> = PublishSubject.create<I>()
override fun accept(intent: I) = subject.onNext(intent)
fun subscribe(): Disposable = subject.subscribe()
fun subscribe(c: Consumer<in I>): Disposable = subject.subscribe(c)
//.. OTHER SUBSCRIBE OVERLOADS
}
subscribe has overloads and, usually, people get used to the subscribe(Consumer) overload. Then they use subjects and suddenly onComplete is also invoked. RxRelay saves the user from themselves who don't think about the difference between subscribe(Consumer) and subscribe(Observer).
Don't forward errors/completions events to the subject:
Indeed, but based on our experience with beginners, they often don't think about this or even know about the available methods to consider.
Don't expose the subject, make your method signature return an observable instead.
If you need a way to send items into the subject, this doesn't work. The purpose is to use the subject to perform item multicasting, sometimes from another Observable. If you are in full control of the emissions through the Subject, you should have the decency of not calling onComplete and not letting anything else do it either.
Subjects have far more overhead because they have to track and handle
terminal event states. Relays are stateless aside from subscription
management.
- Jake Wharton
(This is from the issue OP opened on GitHub and felt it was a more a correct answer and wanted to "relay" it here for others to see. https://github.com/JakeWharton/RxRelay/issues/30)
In addition to #akarnokd answer:
In some cases you can't control the flow of data inside the Observable, an example of this is when observing data changes from a database table using Room Database.
If you use Subjects, executing subjects.getValue will always throw error about null safety. So you have to put "? or !!" everywhere in your code even though you know that it will be not nullable.
public T getValue() {
Object o = value.get();
if (NotificationLite.isComplete(o) || NotificationLite.isError(o)) {
return null;
}
return NotificationLite.getValue(o);
}

How to update an observable manually?

I'm newbie to reactivex and rxscala, and can create an Observable like this:
val observable = Observable[String] { subscriber =>
subscriber.onNext("something")
}
I can put new strings to the subscriber inside the Observable.apply.
Is it possible to update the observable outside? I mean, is there any method like putNext:
observable.putNext("another string")
to make me put new things to the existing observable?
If you want to manually control an Observable, you need to use a Subject.
According to the ReactiveX documentation:
A Subject is a sort of bridge or proxy that is available in some implementations of ReactiveX that acts both as an observer and as an Observable. Because it is an observer, it can subscribe to one or more Observables, and because it is an Observable, it can pass through the items it observes by reemitting them, and it can also emit new items.
You can subscribe to a Subject, but you can also pass new elements to it, de facto controlling it manually, which is what you ask for.
Example:
val subject = PublishSubject[String]()
subject.onNext("one")
subject.onNext("two")
subject.onNext("three")
subject.onCompleted()