I find this very puzzling. Coming from ReactiveCocoa I would expect something like this possible.
How can I initialize the RxSwift observable to 5?
You can create stream in multiple ways:
Main way
Observable<Int>.create { observer -> Disposable in
// Send events through observer
observer.onNext(3)
observer.onError(NSError(domain: "", code: 1, userInfo: nil))
observer.onCompleted()
return Disposables.create {
// Clean up when stream is deallocated
}
}
Shortcuts
Observable<Int>.empty() // Completes straight away
Observable<Int>.never() // Never gets any event into the stream, waits forever
Observable<Int>.just(1) // Emit value "1" and completes
Through Subjects (aka Property / MutableProperty in ReactiveSwift)
Variable is deprecated in RxSwift 4, but it's just a wrapper around BehaviourSubject, so you can use it instead.
There are 2 most used subjects
BehaviorSubject - it will emit current value and upcoming ones. Because it will emit current value it needs to be initialised with a value BehaviorSubject<Int>(value: 0)
PublishSubject - it will emit upcoming values. It doesn't require initial value while initialising PublishSubject<Int>()
Then you can call .asObservable() on subject instance to get an observable.
In RxSwift Variable is deprecated. Use BehaviorRelay or BehaviorSubject
I am not able to test it right now, but wouldn't Observable.just be the function you are looking for?
Source for Observable creation: github link
Of course, you could also use a Variable(5) if you intend to modify it.
As I am learning RxSwift I ran up on this thread. You can initialize an observable property like this:
var age = Variable<Int>(5)
And set it up as an observable:
let disposeBag = DisposeBag()
private func setupAgeObserver() {
age.asObservable()
.subscribe(onNext: {
years in
print ("age is \(years)")
// do something
})
.addDisposableTo(disposeBag)
}
in Rx in general, there is a BehaviorSubject which stores the last value
specifically in RxSwift, there is also Variable which is a wrapper around BehaviorSubject
see description of both here - https://github.com/ReactiveX/RxSwift/blob/master/Documentation/GettingStarted.md
Related
I'm wondering if there is a way in RxSwift to observe value of stored variable property. Eg. in following example:
var updatedValue: Int = 0
var observedValue: Observable<Int> {
return Observable.create({ (observer) -> Disposable in
observer.onNext(updatedValue)
return Disposables.create()
})
}
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
updatedValue = updatedValue + 1;
}
let myObservable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
.publish()
myObservable.connect()
myObservable
.withLatestFrom(observedValue)
.subscribe { (event) in
print(event)
}
We have variable property updatedValue and hot observable myObservable. We also increment value of updatedValue in Timer.scheduledTimer....
Flow here is pretty straight forward. When we subscribe, observedValue gets called, we get onNext from observedValue and then Disposables.create(). Then we print event onNext(0).
As myObservable is based on Observable.interval, same withLatestFrom value gets printed in onNext every second.
Question: Is there a way to print last value of updatedValue every time myObservable emits new event? So instead of 0,0,0... we get 0,1,2...
I'm aware that updatedValue could be declared as BehaviorRelay.
I'm also aware that we could use .map { } to capture self.updatedValue.
But I'm wondering if there is any way to create a Observable wrapper around standard variable property so it calls onNext with most recent value every time trigger sequence sends an event? Without capturing self or changing declaration on updatedValue.
Thanks for any comments and ideas!
RxCocoa has a handy wrapper around KVO. You should be able to use it from .rx extension on NSObject subclasses.
For your issue, I guess you can do something like:
let updatedValueObservable = self.rx.observe(Int.self, "updatedValue")
But I'm wondering if there is any way to create a Observable wrapper around standard variable property so it calls onNext with most recent value every time trigger sequence sends an event? Without capturing self or changing declaration on updatedValue.
The correct answer is, no. There is no way to do anything to updatedValue without involving self. One way of doing it would be with Observable<Int>.interval(1, scheduler: MainScheduler.instance).compactMap { [weak self] _ in self?.updatedValue }.distinctUntilChanged() (Your use of publish and connect is odd and unnecessary,) but that involves self.
Since your property is a value type, the only way to access it is through self, even if Rx wasn't involved at all.
I'm learning RxSwift and I've come across the following pattern when creating Observables:
return Observable.create { observer in
let disposable = Disposables.create()
// Do some stuff with observer here
return disposable
}
As far as I can tell the returned Disposable doesn't actually do anything, does it serve a purpose other than to meet the requirements of the API to return a Disposable?
Is there any scenario where you might need to return a configured Disposable?
I suppose the thing that's confusing me the most is that the returned Disposable seems separate from the implementation of the Observable being created, i.e. it's not assigned to any properties or passed anywhere it's just created and returned.
There are two variations of the create method in relation to Disposables.
The first one, as Daniel mentioned, is used when you create a new Observable; you'll use the Disposables.create { ... } closure to "do cleanup", basically.
This is highly useful when using flatMapLatest, as your previous request will be disposed when a new ones comes in. Whenever it would be disposed, that "clean up" block will be called.
Observable<Int>.create { observer in
let someRequest = doSomeLongRunningThing { result in
observer.onNext(result)
observer.onCompleted()
}
return Disposables.create {
// How can I "cleanup" the process?
// Cancel the request, for example.
someRequest.cancel()
}
}
The second variation of Disposables.create is used for an entirely different purpose - grouping several Disposable objects as a single disposable object (a CompositeDisposable).
For example:
let disposable1 = someAction()
let disposable2 = someOtherAction()
let compositeDisposable = Disposables.create(disposable1, disposable2)
The Disposables.create function takes an optional closure. You should put any cancelation code in that closure. If you don't have any way to cancel, then the code is empty.
A good example is the wrapper around URLSession's dataTask method. In non-Rx code when you call URLRequest.shared.dataTask it returns a URLSessionDataTask object which can be used to cancel the network call. That object's cancel function gets called in the disposable.
Another common use is when you subscribe to some other observable from within your create closure. You then have to pass the disposable from that/those subscriptions by returning a Disposables.create(myDisposable) So that those subscriptions will get canceled properly when your Observable is disposed of.
I'm trying to observe a Variable and when some property of this variable fits a condition, I want to make an "observable" API call and bind the results of that call with some UI element. It is working the way I present it here, but I'm having the thought that it could be implemented way better, because now I'm nesting the subscription methods:
self.viewModel.product
.asObservable()
.subscribe { [weak self](refreshProduct) in
self?.tableView.reloadData()
self?.marketProduct.value.marketProduct = refreshProduct.element?.productId
if refreshProduct.element?.stockQuantity != nil {
self?.viewModel.getUserMarketCart()
.map({ (carts) -> Bool in
return carts.cartLines.count > 0
}).bind(onNext: { [weak self](isIncluded) in
self?.footerView.set(buyable: isIncluded)
}).disposed(by: (self?.disposeBag)!)
}
}.disposed(by: disposeBag)
Is there any other way to do this? I can get a filter on the first observable, but I don't understand how I can call the other one and bind it on the UI.
NOTE: I excluded a few other lines of code for code clarity.
A typical solution would be using .switchLatest() as follows.
create *let switchSubject = PublishSubject<Observable<Response>>()"
bind UI to it's latest value: switchSubject.switchLatest().bind(...
update 'switchSubject' with new requests: switchSubject.onNext(newServiceCall)
I am new to Reactive programming, and I'm trying to observe a boolean value from my ViewModel in order to let my ViewController know when to start/stop the app's loader screen.
It's fairly simple and I want to use this method to avoid unnecessary delegates, since my ViewModel holds the business logic and my ViewController handles the UI.
My problem is this compiler error: Ambiguous reference to member 'subscribe'.
It also adds the two possible candidates, as you can see in the image below:
In my ViewModel, I've declared the observable as PublishSubject:
let done = PublishSubject<Bool>()
And I use it while observing another stream:
func subscribe() {
done.onNext(false)
anotherObservable.subscribe(
// other events observed here but not relevant to this matter
onCompleted: {
self.done.onNext(true)
}).addDisposableTo(rx_disposeBag)
}
And, finally, this is how I'm trying to handle it in the ViewController:
self.model.done.subscribe(
.onNext { isDone in
if isDone {
self.removeLoader()
}
}).addDisposableTo(rx_disposeBag)
I believe there is something simple I'm probably missing, so any help is appreciated.
In your second subscribe should be:
self.model.done.subscribe(onNext: { isDone in
if isDone {
self.removeLoader()
}
}).addDisposableTo(rx_disposeBag)
Many posts seem to advise against notifications when trying to synchronize functions, but there are also other posts which caution against closure callbacks because of the potential to inadvertently retain objects and cause memory issues.
Assume inside a custom view controller is a function, foo, that uses the Bar class to get data from the server.
class CustomViewController : UIViewController {
function foo() {
// Do other stuff
// Use Bar to get data from server
Bar.getServerData()
}
}
Option 1: Define getServerData to accept a callback. Define the callback as a closure inside CustomViewController.
Option 2: Use NSNotifications instead of a callback. Inside of getServerData, post a NSNotification when the server returns data, and ensure CustomViewController is registered for the notification.
Option 1 seems desirable for all the reasons people caution against NSNotification (e.g., compiler checks, traceability), but doesn't using a callback create a potential issue where CustomViewController is unnecessarily retained and therefore potentially creating memory issues?
If so, is the right way to mitigate the risk by using a callback, but not using a closure? In other words, define a function inside CustomViewController with a signature matching the getServerData callback, and pass the pointer to this function to getServerData?
I'm always going with Option 1 you just need to remember of using [weak self] or whatever you need to 'weakify' in order to avoid memory problems.
Real world example:
filterRepository.getFiltersForType(filterType) { [weak self] (categories) in
guard let strongSelf = self, categories = categories else { return }
strongSelf.dataSource = categories
strongSelf.filteredDataSource = strongSelf.dataSource
strongSelf.tableView?.reloadData()
}
So in this example you can see that I pass reference to self to the completion closure, but as weak reference. Then I'm checking if the object still exists - if it wasn't released already, using guard statement and unwrapping weak value.
Definition of network call with completion closure:
class func getFiltersForType(type: FilterType, callback: ([FilterCategory]?) -> ()) {
connection.getFiltersCategories(type.id).response { (json, error) in
if let data = json {
callback(data.arrayValue.map { FilterCategory(attributes: $0) } )
} else {
callback(nil)
}
}
}
I'm standing for closures in that case. To avoid unnecessary retains you just need to ensure closure has proper capture list defined.