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

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.

Related

Subscribe cancels the subject when the source publisher completes

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.

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()
}

What is the difference between PublishSubject and PublishRelay in RxSwift?

I am new to RxSwift programming.
I am confused between the two while coding. Which one should be used to store datasource of table and how to decide that ?
A PublishSubject can emit an error or completed event while a PublishRelay cannot.
A PublishSubject conforms to the ObserverType protocol while the PublishRelay does not.
Another important point that was alluded to by #RobMayoff in his comment. Neither a PublishSubject nor a PublishRelay stores state, so neither of them are a good idea to "store datasource of table".
Fortunately, you don't need to store the state yourself because the DataSource object that the items operator creates internally stores it.
In other words, you don't need to use a Subject or Relay (of any sort) to feed a table view. Just use an Observable.
If you look at the interface to PublishRelay you can see that it wraps a PublishSubject but it hides this from its interface. So you can only send it accept(_ event: Element) which means you cannot send it error or completed Events only next elements.
public final class PublishRelay<Element>: ObservableType {
private let subject: PublishSubject<Element>
// Accepts `event` and emits it to subscribers
public func accept(_ event: Element) {
self.subject.onNext(event)
}
/// Initializes with internal empty subject.
public init() {
self.subject = PublishSubject()
}
//...
}
Anyhow, if you look at examples of tableview using RxCocoa they just wrap an array as an Observable usually using Just or create that you then pass to the tableview using RxCocoa's interface. You don't really want a Subject just a plain observable.

How to expose different functionality of a single type to different modules?

I'm working on a small 2-player card game app for iOS. For now, I've separated this app into 3 modules.
UI
Game logic
Networking
I have two pubic protocols in the networking module:
public protocol ConnectionManager {
init(name: String)
var peers: Property<[String]> { get }
func invitePeer(at index: Int)
func respond(accept: Bool)
var invitation: Signal<String, NoError> { get }
var response: Signal<Bool, NoError> { get }
}
public protocol MessageTransmission {
func send(_ data: Data)
var data: Signal<Data, NoError> { get }
}
and there is actually only one concrete implementation which conforms to both two protocols. Lets' say:
class Foo: ConnectionManager, MessageTransmission {
// ... codes omitted
}
The UI module receives a name from the user and uses it to initialize a Foo. Then it displays the nearby players according to the peers property. When a user commits an invitation to start a new game, UI module forwards this request to the ConnectionManager and the ConnectionManager handles those dirty works.
For the game logic module, it only cares about message transmission, but the transmission depends on the previous "invite-respond" step because we need a target to exchange message with. (the target related concepts are encapsulated, so the game logic only knows there is a thing that it can send message to and receive message from.)
My thought is: once a session is established, i.e., once an invitation is responded with true, the UI module initializes a game logic thing (maybe an instance of a type Game) and passes the ConnectionManager (although UI module initializes an instance of type Foo, it stores that instance as of type ConnectionManager) to it. But the problem is, the ConnectionManager has nothing to do with MessageTransmission when looked from the outside even if they're implemented by a single type internally.
I can do a force case at either side, but it looks tricky.
I can combine those two protocols, but then the UI module and the game logic module interacts with a surplus interface (interface that has more features than needed).
Which path should I take? or is there any better way to do this?
ps: The Foo can only be initialized by UI module because it's the only one who knows about the name. And it's also necessary to pass the same instance to the game logic module because there are some internal states (the target related thing) which is modified in the previous interaction with UI module and effects the later interaction with game logic module. I can't think of another way to do this.
As I understood from your explanation you need to pass MessageTransmition implementation to game logic but do not want to pass ConnectionManager.
As soon as instance of MessageTransmission depends on result of invitation (or accepting invite) made by ConnectionManager you need to build object conformed to MessageTransmition from within ConnectionManager and pass to game logic. Yes, that means that you couple these two entities but they will have clean responsibilities in this case.
Consider this kind of protocol:
protocol ConnectionManager {
func respond(accept: Bool) -> Signal<MessageTransmition, Error>
}
respond() method accepts invitation and builds MessageTransmission conforming object on success or returns error if accepting failed or if invitation declined. This object you can pass to game logic.

Does a read only BehaviorSubject interface exist in RX and if not, is it a bad idea to make one?

Implementations of rx provide BehaviorSubject<T> and Variable<T> as mechanisms for modeling properties that change over time (a useful replacement for C# INotifyPropertyChanged).
Generally these are exposed as Observable<T> but it would be more useful to expose properties as something like:
class ObservableValue<T> : Observable<T>{
var currentValue:T { get }
}
This can be created along these lines in swift:
class ObservableValue<Element> : ObservableType {
typealias E = Element
private let subject:BehaviorSubject<E>
var currentValue:E {
get {
return try! subject.value()
}
}
init(subject:BehaviorSubject<E>) {
self.subject = subject
}
func subscribe<O: ObserverType where O.E == E>(observer: O) -> Disposable {
return self.subject.subscribe(observer)
}
}
Does this already exist? and if not is it because it's against the aims of Rx?
The only way around it is to expose a separate currentValue or write consumers that assume the concrete implementation behind the exposed Observable is a BehaviourSubject or somewhere in the chain a replay() has occured e.g. the following snippet doesn't make it explicit that as soon as I subscribe I will get a value:
class MyViewModel {
// 'I will notify you of changes perhaps including my current value'
myProperty:Observable<String>
}
so code has to be written as if its 'asynchronous' with an underlying assumption it will act in an almost synchronous manner rather than:
class MyViewModel {
// 'I have a current value and will notify you of changes going forward'
myProperty:ObservableValue<String>
}
Having thought it over and discussed it a bit more presumably the reason it doesn't (and perhaps shouldn't exist) is that it's an introduction of imperatively accessed state.
Other mechanisms of maintaining state (such as scan) do so within the confines of chained observables rather than as 'dead-end' direct calls such as 'give me the value right now'.
Perhaps it would have it's place in a hybrid reactive/imperative approach but it may just hinder full embracement of the reactive style.
It's analogous to using promises or tasks in half of the code then reverting to synchronous blocking code in other parts.
In most cases what people do is create a standard view model that exposes properties via INotifyPropertyChanged. This allows UI elements to bind to them and receive property change events and keep the UI in sync.
Then if you want an IObservable for said property you take advantage of standard Rx operators that turn events into IObservable. You can google this to find lots of different implementations. You would generally create and consume these observables from something that is observing the view model rather than expose them on the view model directly.