Closed zdnk closed 8 years ago
*Subject
are more intended as a way to customize behaviors of certain operators.
BehaviorSubject
can be used in similar scenarios as Variable
Variable
exists because people usually have a hard time finding BehaviorSubject
and Swift has additional complexity of memory management.
Variable
is just a thin wrapper around a private instance of BehaviorSubject
Variable
doesn't have an interface that enables erroring out observable sequence, so that's additional compile time guarantee vs using a BehaviorSubject
.Variable
will also complete sequence when it's deallocated and BehaviorSubject
won't.The reason why variable needs to be transformed using asObservable
interface is because in that way we can assure that you can do this:
let varA = Variable(23)
let varB = Variable(false)
let button = UIButton()
_ = Observable.combineLatest(varA.asObservable(), varB.asObservable()) { (a, b) -> Bool in
if a > 0 && b == true {
return true
}
return false
}
.bindTo(button.rx_enabled)
// .addDisposableTo(disposeBag) <-- no need to add to dispose bad for this particular case
This is a good and a bad thing.
So in short:
multicast
)BehaviorSubject
if you like)The reason why we have created Driver
is to help people use the compiler to prove certain properties of their programs. I would personally use it to model stateful abstractions in the UI layer. That's why we've created it.
I would also suggest people to create their own abstractions that express properties they require and wrap observables. We've been recently asked will there be units like Single
in this project. That's another excellent example of highly useful unit, but we won't implement it directly in project, although I can see myself creating a small Single
unit, or someone else creating it, and publishing it to github.
My advice is to use the compiler as much as you can, but how individuals use it, it's up to them.
I wouldn't want to comment on approaches that external libraries (like Bond) or example apps (ReactiveWeatherExample) take because we haven't been related in any way with them.
We've tried to add a lot of example usages to RxExample app, so if there is some question about some of examples there, I would be happy to answer them.
OK, I think I am a little bit closer to understanding the whole concept.
Another question popped in my head while reading your answer. What if I would like to implement something like Promise pattern? Meaning I have some async tasks that I need to have executed serially dependent on each other, obviously I can achieve this by concat
I suppose? But how could I implement something a little bit cleaner for chaining tasks, or could you point in some direction or to some example of chaining Observable
s that run and complete serially?
I see, Variable
is sort of storage type like variable or constant in Swift. It definitely looks better and more clean in code than BehaviorSubject
or PublishSubject
for example.
What is best practice in FRP with RXSwift then? Having all properties in classes, controllers, view models as Subject
s and/or Unit
s?
The answer to first question is actually flatMap
instead of concat
:)
All of these things you have mentioned are equivalent to observable sequences, and thus composable. I would probably prefer Variable
over BehaviorSubject
because of the mentioned compile time guarantees.
We've tried to explain and provide examples how one could best use other concepts in RxExample app. I'm not sure there is a better way how I can explain where to use which concept other then those examples and all documentation we have.
The general rules of thumb are:
Observable
, Driver
) as much as possible because they are pure definitions and immutable. If you expose Variable
/Subject
s, then you lose declarative nature of computation since anyone can publish values from any part of the system (anyone can call on*
and thus cause unanticipated effects because they have an observer interface also).Driver
is more natural then using Observable
sequences in raw form since it communicates intent better, shares computation resources (like you would intuitively expect) and has additional compile time guarantees (main thread, can't error out).Variable
/Subject
s because there are things like cell reusing, you need to use some legacy code or interact with some messy part of system, etc .... And sometimes it's obvious how can values be set, so it's maybe ok-ish, but try to use them in a few places as possible.Some exposed properties are supposed to have read-write access, Other read-only and the rest of them are private.
So possibly for public read-only case I could do something like:
private let _property = Variable(true)
public var property: Driver<Bool> {
return _property.asDriver()
}
Would that be correct approach?
Hi @zdenektopic ,
your suggestion will probably work, but this is not idiomatic for sure and not an approach I would suggest people to take.
This is one of the examples in the example app:
It also has an explanation
/**
Notice how no subscribe call is being made.
Everything is just a definition.
Pure transformation of input sequences to output sequences.
*/
There is also the same example using vanilla observable sequences:
These are kind of ideal examples IMHO.
Important things:
Variable
s, Subjects
....subscribe
, bind
, drive
methods or subscriptions being made in any way.DisposeBag
s, etc .., no resource management is needed because all operators are already chaining disposing mechanism.If you are more into Redux kind of architectures, take a look at calculator example:
https://github.com/ReactiveX/RxSwift/tree/master/RxExample/RxExample/Examples/Calculator
Thanks, helped me a lot to understand how ViewModels should be built using RxSwift! :)
If you have some commands/actions in view model that come from view controller, you have to have subscribe
in the view model code, don't you?
Hm, why do you think you need to subscribe
in view model? I don't remember we've called subscribe
, drive
or bind
in any view model in RxExample app.
Word never
is a tricky word and I don't like to use it, maybe I should say almost never to be more precise :)
This article uses behavior subjects extensively. Would you say that is not the ideal implementation?
https://medium.com/cobe-mobile/implementing-mvvm-in-ios-with-rxswift-458a2d47c33d#.oucl7alcp
@mingyeow I think that code could be improved, so yes, it's probably not the most ideal implementation IMHO.
We can probably close this one and reopen if needed.
Hi @kzaher!
An example with no subscribe
, bind
, drive
looks interesting, but one thing really embarrasses me - you must have a loaded view to configure your view model. For example, if I want to create a view model out side of a view controller (e.g. build a whole module using builder pattern), it will call loadView
before it's really needed. Don't you think it's a problem?
I will second to @serejahh, in real world application you most likely want to create ViewModel outside of the ViewController due to dependencies coming from somewhere else that needs to be DI'ed to the ViewModel itself before the viewDidLoad is called
For very simple applications the approach works fine when viewModel itself can be constructed without any external dependencies but thats not the case with most of the real world applications
I think for that either Subject or Variable seems the only way to model this use-case, although I would only expose them as observables and keep the rest internal to viewModel
@serejahh @Vasant-Patel A good way to get around the need for a loaded view before initing your view model is described in this blog post - see the BaseStage
implementation (the naming is different in that post but it's basically just ViewModels.)
It allows the ViewModel to be created in a closure passed to the init of the ViewController like so:
let viewController = AuthenticationLandingViewController.create { (viewController) -> AuthenticationLandingViewModelType in
let viewModel = AuthenticationLandingViewModel(viewActions: viewController.actions)
// any extra config of the VM here
return viewModel
}
@serejahh I see what you mean. If you guys interested I've came up with this idea
@sergdort this is great.
I'm a bit late to the conversation, and I haven't read the blog post from @iandundas or the gist from @sergdort , but I'd like to share my current approach: I'm using Swinject and passing down a Resolver
, which the view controller uses to create a view model passing the arguments required by that view model. This way you create the view model once the view is already created, and have a view model created with its needed dependencies injected. You could even pass down a Resolver
configured with different providers in case you want to implement some unit or integration tests for the view controller. What do you think?
Hello.
I will start with what I know.
If I understand it correctly,
Variable
is type that allows storing value (read-write access) and wrapsBehaviorSubject
which I have no idea what is for. AndObservable
is type that allows read-only access.My issue is regarding combineLatest operator which in
Rx.playground
allows to combine multipleBehaviorSubject
s, but notVariable
s.Variable
s can be alsoObservable
s visasObservable()
method. Is there any difference betweenBehaviorSubject
andVariable
? Which should I use for common storing of values and objects in myUIViewController
s? Or should I use otherSubject
? What if I want to have publicly read-onlyVariable
/other subject/Observable
and other read-write also public?Another thing is
Driver
or other Units. Should I Always useDriver
when working with UI elements? How? Should I haveVariable
or otherSubject
and useasDriver()
every time I need all shit going on UI/main thread? In ReactiveWeatherExample there is notDriver
used inUIViewController
and it is instead manually ensured, the code executes on UI thread. For example in Bond you use Observable type to for all - storing values and subscribing for events/new values.My current code with combineLatest: