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
Related
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.
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.
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
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)
}
I'm creating a custom generic TableView model to work with MVVM and RxSwift. I'm aware of the RxTableViewSectionedReloadDataSource class but not willing to use it now in my project.
The model is created and working
typealias TableViewModel = TableModel<CellDescriptor>
class TableModel<T> {
var sections = [SectionModel<T>]()
func add(item: SectionModel<T>) {
sections.append(item)
}
// More funcs there...
}
I created an instance inside my ViewModel as such :
var tableViewModel = Variable<TableViewModel>(TableViewModel())
And then listen to event in my viewController
viewModel.tableViewModel.asObservable().subscribe({ [weak self] value in
self?.tableView.reloadData()
}).addDisposableTo(dispose)
Several questions here (I'm fairly new to FRP)
:
How can I "emit" an event to trigger my subscription in the viewController from my custom class?
I know that a solution would be to create my sections array as a RxSwift Variable() and then listen to it directly, but wonder if there's a way to make a class (or struct) itself Observable.
Is there a better approach to this problem ? I went through the RxSwift playground and example project but it sounds like there's a thousand ways to do the same thing. Maybe an Observable is not what i would be looking for. :)
Don't make the view model itself an Observable. It's unnecessary complexity and it's just not intuitive. If you really wanted to, then take a look at how Variable is implemented, which should give you an idea of how to do it.
Instead, just use a subject (such as Variable) to hold onto your data within your view model.