How to observe properties or instance variables with ReactiveCocoa - ios

I would like to monitor the number of tableview cell and once it becomes zero (delete all the rows) my button would be disabled immediately otherwise (insert a new row) it will be enabled.
And I would like to do this with ReactiveCocoa.
I'm a newbee with RAC and what I tried is like this:
let count = NSNumber(integer: self.records!.count)
let countSignal: RACSignal = count.rac_willDeallocSignal();
countSignal.subscribeNext { (AnyObject) in
NSLog("here i am")
self.navigationItem.rightBarButtonItem?.enabled = AnyObject.integerValue > 0 ? true : false;
}
But it didn't work.
So far I know how to generate signals and monitor the change with some text fields cause it just comes like this:
self.myTextField.rac_textSignal
But what if I want to product signals or monitor the change of properties or variables so I could subscribe and pass the signals on and do some callback based on their changes?

You will get a value changed signal by using rac_valuesForKeyPath method to observer the property value,here is an tutorials:SWIFT AND REACTIVECOCOA,in Objective-C there is macro RACObserver(target,key path)

Related

Collecting stored variable property using withLatestFrom

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.

Firebase setValue method is taking time

I am using firebase in my iOs app.
When I use the setValue method for updating node value, it takes around 5-10 secs to update value.
How can I overcome this delay?
Code
let shiftReportsRef = Database.database().reference().child(Constants.FireBase().users).child(Constants.FireBase().shiftReports).child(firebaseToken).child(cashierId.stringValue).child(todayDate).child(posPointId.stringValue).child(shiftid.stringValue)
shiftReportsRef?.observe(.value, with: { (peakHoursSnapShot) in
self.shiftDetails = peakHoursSnapShot.value as? [String: AnyObject] ?? [:]
let lastUpdated = Date().gmtDateTime(.dateTimeSec)
self.shiftDetails!["lastUpdated"] = lastUpdated as AnyObject
if self.hasUpdatedValues == false
{
self.updatePeakHoursAndLastUpdateDate(amount, refndedAmount, pymtType, timeSlt)
}
self.hasUpdatedValues = true
})
The code in the question is a little unclear as to what the expected outcome is. Let's walk through it...
First shiftReportRef is defined
let shiftReportsRef =
Database.database().reference().child(Constants.FireBase().users).child(Constants.FireBase().shiftReports).child(firebaseToken).child(cashierId.stringValue).child(todayDate).child(posPointId.stringValue).child(shiftid.stringValue)
then an an observer is added to that ref. The observer is .value which will observe any changes to that node and return them in a snapshot via an event
shiftReportsRef?.observe(.value
But then when the first event occurs and the code within the closure executes, the data at that location is overwritten
shiftReportsRef?.setValue(self.shiftDetails)
which then causes another .value event to fire, therefore overwriting the node again (I think you can see where this is going). Over and over.
So this isn't exactly an answer because we don't know the expected outcome but that's probably why there are delays in the app.

iOS Realm detect changes for RLMObject

I have written code like this to listen for changes in Post object.
notification = Post.allObjects(in: RLMRealm.encryptedRealm()! as! RLMRealm).addNotificationBlock({ (results, changes, error) in
let pred = NSPredicate(format: "tag == %#", self.postTag)
self.posts = CommonResult.objects(with: pred).sortedResults(usingKeyPath: "id", ascending: true)
if let _ = changes {
if (changes!.insertions.count > 0 || changes!.deletions.count > 0 || changes!.modifications.count > 0) {
self.tblListing.reloadData()
}
}
})
In my Post object, there are 2 property. One is 'rowHeight' and another is 'isLikeByMyself'.
I want to reload tableview only if 'isLikeByMyself' is changed. How shall I do? Is it possible?
You have at least two options.
If you don't have many Post objects, you may want to consider registering object notifications on each of them. Object notifications tell you which properties were changed and how, so you can use that information to reload the table view. However, you will need to register a separate notification on each Post object, which may not be practical if you have a large number of them.
Here is an alternate idea. Add an ignored boolean property to Post called something like isLikeWasChanged, and add a Swift didSet block that will set isLikeWasChanged to true any time you modify isLikeByMyself. Then, in your existing collection observation block, reload the table view only if at least one isLikeWasChanged is true, remembering to set them all back to false before you leave the block.

How do I initialize an observable property in RxSwift?

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

Associate slider to a Float property

I am working on an iOS project.
I have a UISlider on my ViewController.
I want to bind a Float property (var) to the current slider value.
If I change this property value, I want to automatically update my slider position.
If I move my slider, I want to automatically update property value.
I have made it work manually with delegates et stuff, but I want to know if there is a simplest way.
In fact, I have many (very huge) UISliders, and the values can change from other part of program.
I know in C# for example it is possible to observe a value and bind it with UI components. I want to do the same thing in swift
There are many ways to do this and many libraries that help you with this task.
I like Bond.
It lets you observe property values without the didSet boilerplate and helps you keep your code clean.
For example, this does exactly what you are looking for:
import Bond
let ageSlider = UISlider()
let age = Observable<Float>(0)
ageSlider.bnd_value.bidirectionalBindTo(age)
This helps you keeping the MVC or MVVM pattern, because changing the model property will also update the UI right away (and viceversa!):
age.Value = 18
print(ageSlider.value) // Prints 18
You can also combine values into a single one:
let slider1 = UISlider()
let slider2 = UISlider()
let averageValue = combineLatest(slider1.bnd_value, slider2.bnd_value).map{ (v1, v2) in (v1 + v2) / 2 }
averageValue.observe { newValue in
print("New average value is \(newValue)")
}
More info in the project page.

Resources