Im new to RxSwift and what Im trying is perform the following,
User type is search UiTextField
App must wait 1 sec after user finish typing to make sure not to hit the Api with each letter write or delete button pressed.
App hit my Api for search
I tried the below code as example
class DestinationSearch: UIViewController {
let searchTF = UITextField()
var disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
setUpUI()
searchTF.rx.controlEvent(.editingChanged)
.throttle(.milliseconds(1000), scheduler: MainScheduler.instance)
.withLatestFrom(searchTF.rx.text)
.subscribe(onNext:{ query in
print(self.hitSomeApi(query: query ?? "--"))
}).disposed(by: disposeBag)
}
func hitSomeApi(query: String) -> String {
return query + " Response from API"
}
When I run the app and start typing I get the the Response from API message with each letter or backspace button pressed! why the throttle delay is not working? Im I doing something wrong here?
Any help will be much appreciated
Based on your description of the problem, it seems like the debounce operator is more suitable than throttle. debounce only emits an element if a certain time, in which nothing has been emitted, has passed, whereas throttle ensures that elements are emitted at least a certain time interval apart.
I can confirm that your code using throttle is working as I'd expect - if I type very quickly, the "Response from API" messages appear roughly every 1 second. If I type very slowly, slower than 1 keystroke per second, then the messages come whenever I press a key. In other words, whenever there is an editing changed event, throttle checks to see if there was a previous one less than 1 second ago. If there is, ignore this new one.
If you use debounce however (the same code, just replace throttle with debounce), then after each keystroke, it would wait for 1 second to see if the text field is going to change again. It would only emit the editing changed event if it has waited and there is no more editing changed events. So if you keep typing at a pace higher than 1 keystroke per second, no elements will ever be emitted by the observable. This seems to be what you want.
You can compare these two operators on RxMarbles (they are called slightly different names there):
debounceTime
throttleTime
Related
What I'm wanting to accomplish is save a score to firebase that has two values attached to it. Here's the code that writes the score to firebase.
func writeToFirebase() {
DispatchQueue.global(qos: .userInteractive).async {
self.ref = Database.database().reference()
if GameManager.instance.getTopScores().contains(GameManager.instance.getGameScore()) {
self.ref?.child("user")
.child(GameManager.instance.getUsername())
.child(String(GameManager.instance.getGameScore()))
.updateChildValues( [
"badge":GameManager.instance.getBadgeLevel(),
"vehicle": GameManager.instance.getVehicleSelected()
]
)
}
}
}
The issue I'm having is when a new score is saved with its values it sometimes overwrites the other scores. This seems to be random and its not when they're the same score or anything like that. Sometimes it will only overwrite one score and sometimes multiple. I'm watching firebase and I can see it being overwritten, it turns red and then is deleted. Sometimes the new score being added will be red and get deleted. The score doesn't need to be a child, but I don't know how to attach values to it if it's not. Any help is appreciated
This issue seems to happen occasionally so I am going to post my comment as an answer.
There are situations where an observer may be added to a node and when data changes in that node, like a write or update, it will fire that observer which may then overwrite the existing data with nil.
You can see this visually in the console as when the write occurs, you can see the data change/update, then it turns red and then mysteriously vanishes.
As suggested in my comment, add a breakpoint to the function that performs the write and run the code. See if that function is called twice (or more). If that's the case, the first write is storing the data properly but upon calling it a second time, the values being written are probably nil, which then makes the node 'go away' as Firebase nodes cannot exist without a value.
Generally speaking if you see your data turn red and vanish, it's likely caused by nil values being written to the node.
Using Core Bluetooth with Swift 3, I use CBPeripheral.writeValue() to write a characteristic to the Central, and I do this when the value of a UISlider changes. I was noticing that, even when dragging the slider slowly, occasionally a jump in value would be seen on the Central. I thought perhaps some over-the-air corruption was occurring, so I changed the characteristic to write the same value three times. Now, only if all the values received by the Central match will it act on them. Here's the current code:
#IBAction func slideValChanged(_ sender: UISlider)
{
let sliderVal = UInt8(sender.value.rounded(FloatingPointRoundingRule.down))
if (sliderVal != self.sliderVal)
{
self.sliderVal = sliderVal
self.bytes.removeAll()
self.bytes = [self.sliderVal, self.sliderVal, self.sliderVal]
DispatchQueue.global(qos: .userInitiated).async
{
self.data = Data(bytes: self.bytes)
self.peripheral.writeValue(self.data, for: self.writeCharacteristic!, type: CBCharacteristicWriteType.withResponse)
print("Write values sent:", self.bytes[0], self.bytes[1], self.bytes[2])
}
}
}
Even with this, I still see the value jump, and not to anything particular. The print() statement always prints the same (and correct) number three times. Similarly, on the Central, when the value jumps, I receive three equal but incorrect values. How can this be? All I can think is that something in Core Bluetooth is changing the value before it is put on air, but I'm not sure, and I don't know where to focus my attention.
As you are using CBCharacteristicWriteType.withResponse, even slow movement of your slider will send to many packets.
When you write to the writeCharacteristic command you need to wait for a callback before calling writeCharacteristic again. The callback will indicate that your data has been buffered in the Central BLE-stack and the stack is ready to receive additional data. Without this Your data can be corrupted or even lose your connection.
I'm learning RxSwift, what I would like achieve is to get mechanism which prints me text from UITextFied, but after given interval.
How it works now: when I type first character this character is immediately printed out (not after delay how I expected) and if I keep on typing long sentence, text is printed after each two second (as interval is set in throttle), but I would like to have only latest text value.
My code:
inputField.rx.text.orEmpty
.throttle(2, latest: true, scheduler: MainScheduler.instance)
.subscribe(onNext: { text in
print("\(text)")
}, onDisposed: nil)
.addDisposableTo(disposeBag)
Im looking for your help Rx fellows :) Thanks
Use debounce instead of throttle.
From the documentation for debounce:
Ignores elements from an observable sequence which are followed by
another element within a specified relative time duration, using the
specified scheduler to run throttling timers.
From the documentation for throttle.
Returns an Observable that emits the first and the latest item emitted
by the source Observable during sequential time windows of a specified
duration.
I'm trying to make a spaceship move on the screen with the keyboard. I manage to deal with key events, but I noticed that when multiple keys are kept down at the same time, it won't behave correctly and only one will have priority. I'm using a switch statement because I thought the keyDown function was called once for every key, but even when I explicitly add a fallthrough in the cases, it's not better. Has anyone ever experienced anything like that and is there any better way to use the keyboard as a controller?
override func keyDown(with event: NSEvent) {
switch event.keyCode {
case 0x31:
if let playerShip = self.playerShip {
playerShip.run(SKAction.init(named: "Pulse")!, withKey: "fadeInOut")
}
case 123:
if let playerShip = self.playerShip {
playerShip.run(SKAction.applyAngularImpulse(0.001, duration: 0.1))
}
case 124:
if let playerShip = self.playerShip {
playerShip.run(SKAction.applyAngularImpulse(-0.001, duration: 0.1))
}
case 125:
if let playerShip = self.playerShip {
playerShip.run(SKAction.applyImpulse(CGVector.init(angle: playerShip.zRotation).opposite(), duration: 0.1))
}
case 126:
if let playerShip = self.playerShip {
playerShip.run(SKAction.applyImpulse(CGVector.init(angle: playerShip.zRotation), duration: 0.1))
}
default:
print("keyDown: \(event.characters!) keyCode: \(event.keyCode)")
}
}
My guess is that even if you get the code working exactly the way you describe it will be extremely difficult in practice for anyone playing your game to hit two keys at the exact same time. One key will almost always be hit slightly before the other. So, maybe you could implement it so that you capture one key event, and then look for a second one happening within a short time window after the first one (before the first one is released).
The keyDown method should be called by the OS X every time a key is pressed. If two keys are pressed, it's called twice, etc. Perhaps you're seeing this behavior because only the last key's action overrides the first key's action?
After testing a few things to make sure that my SKActions were not the cause of the problem, I remembered a few things from the days I was coding in Delphi with DelphiX and GLScene. I know it's PC, but it's related.
The thing is that the keyboard event cue will retrigger only the las key that was pressed. So applying force with the up arrow and keeping it pressed to accelerate will work until I press, for example, the left arrow to apply some torque. Then the left arrow key will get retriggered, but the next event to come from the up arrow, even if it's still pressed by now, will be when you actually release it. Therefore, the ship will start rotating but will stop accelerating because the keyDown event won't get retriggered for the up arrow.
This is why you need a way to keep track of key states, so you can check if multiple keys are pressed together at any given moment.
This is also why I'm gonna build my keyboard manager class.
I am wanting to know a button is rendered on main window UI or not. This button rendering is depending on server response result (written in Objective C). If server response comes perfectly it becomes render perfectly (VISIBLE) otherwise it is not present there (INVISIBLE). And whenever it becomes visible I always tap on it for further next process.
I wrote code
UIATarget.localTarget().pushTimeout(200);
//My code
UIATarget.localTarget().popTimeout();
By the above code I have to wait till 200 sec but my concern is I want to wait but whenever object is on screen I don't want keep me busy in WAITING MODE.
How will I write code in automation?
Thanks
Ok, this might give you idea how to follow-up:
For your view implement an accessibilityValue method which returns a JSON formatted value:
- (NSString *)accessibilityValue
{
return [NSString stringWithFormat:
#"{'MyButtonisVisible':%#}",
self.MyButton.isHidden ? #"false" : #"true"];
}
Then somehow you can access it from your test javascript:
var thisproperty = eval("(" + element.value() + ")");
if (thisproperty.MyButtonisVisible) {
UIATarget.localTarget().tap({"x":100, "y":100});
}
Hope that helps.
If you make the name different when you enable the button you can do this:
var awesomeButton = target.frontMostApp().mainWindow().buttons()[0];
UIATarget.localTarget().pushTimeout(200);
awesomeButton.withName("My Awesome Button");
if (awesomeButton.isVisible()) {
UIALogger.logError("Error no awesome button!");
}
UIATarget.localTarget().popTimeout();
withName will repeatedly test the name and control will return to your script once the name matches or when the time out is reached.
Per Apple's Doc
withName:
Tests if the name attribute of the element has the given string value. If the match fails, the test is retried until the current timeout expires.
Timeout Periods:
If the action completes during the timeout period, that line of code returns, and your script can proceed. If the action doesn’t complete during the timeout period, an exception is thrown.
https://developer.apple.com/library/etc/redirect/xcode/ios/e808aa/documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/UsingtheAutomationInstrument/UsingtheAutomationInstrument.html#//apple_ref/doc/uid/TP40004652-CH20