Duplicate event when editingDidEnd called on UITextField RxSwift - rx-swift

I have a text field that is used to capture the users' login name.
The user taps inside of the text field. The text field emits an empty string event.
The user then taps in some text ("123"). The text field emits an event for each character.
The user then taps inside of another text field. The text field emits an event that represents the latest state of the text field ("123").
I'm wondering why the text field emits the last event, and is there a way to suppress the last event. I tried adding distinctUntilChanged but that had no effect. I would think that distinctUntilChanged would not fire the last event since it is the same as the previous event.
let userNameStream: Observable<String> = userNameText
.rx
.text
.orEmpty
.skip(1)
.debug("userNameStream")
.asObservable()
.share(replay:1)
.distinctUntilChanged()
The output
018-02-21 06:05:29.303: userNameStream -> Event next()
userNameText : editingDidBegin
2018-02-21 06:05:38.448: userNameStream -> Event next(1)
2018-02-21 06:05:43.369: userNameStream -> Event next(12)
2018-02-21 06:05:48.880: userNameStream -> Event next(123)
2018-02-21 06:06:03.224: userNameStream -> Event next(123) // Duplicate Event!
userNameText : editingDidEnd

Move the debug operator after the distinctUntilChanged operator
let userNameStream: Observable<String> = userNameText
.rx
.text
.orEmpty
.skip(1)
.distinctUntilChanged()
.debug("userNameStream")
.asObservable()
.share(replay:1)

I am using: RxCocoa (4.1.2), RxSwift (4.1.2)
Please try the following code:
let disposeBag = DisposeBag()
userNameText
.rx
.text
.orEmpty
.distinctUntilChanged()
.debug()
.subscribe()
.disposed(by: disposeBag)
Log
2018-02-22 11:47:21.618: ViewController.swift:26 (viewDidLoad()) -> subscribed
2018-02-22 11:47:21.620: ViewController.swift:26 (viewDidLoad()) -> Event next()
2018-02-22 11:47:29.560: ViewController.swift:26 (viewDidLoad()) -> Event next(a)
2018-02-22 11:47:30.046: ViewController.swift:26 (viewDidLoad()) -> Event next(ab)
2018-02-22 11:47:30.464: ViewController.swift:26 (viewDidLoad()) -> Event next(abc)
If you have problems please post your library version so I can help.

Related

How Can I use the Bind Method in RXSwift?

The following sample code throws an error stating
No exact matches in call to instance method 'bind'
How can I bind the onNext of my publish subject to my observable?
let publish = PublishSubject<Void>()
// Error Here
publish.bind(to: myMethod())
func myMethod() -> Observable<Void> {
return Observable.create{ observer in
observer.onNext(())
observer.onCompleted()
return Disposables.create()
}
}
So what I want is everytime my publish subject emits an onNext event I want to trigger the observable of 'myMethod'
I'm not sure how to interpret this, but it sounds like you want something like:
let publish = PublishSubject<Void>()
let response = publish.flatMap { myMethod() }
response
.bind(onNext: { print($0) })
func myMethod() -> Observable<Void> {
Observable.create{ observer in
observer.onNext(())
observer.onCompleted()
return Disposables.create()
}
}
But it all seems rather pointless since all myMethod() does is emit a next event.
First, the bind(to:) method is in the RxCocoa Framework so you need to add import RxCocoa in your file.
Then, the observable created by myMethod will only be able to emit the event from the .create block. You can't use bind on it. If you need an observable with both the events from your subject and from the myMethod observable, you can do that :
let thirdObservable = Observable.merge(publish, myMethod())

Why consume() on a KeyEvent in an event filter not working?

I want to filter some characters i.e. the letter "a" in a TextField. I explicitly don't want to use the recommended TextFormatter / setTextFormatter() for this task.
The code sample below should actually consume the event on the event-dispatching chain before it arrives to the TextField node, which is a child node of parentNode, but it doesn't. Same happens if I set the filter on the textfield node itself of course.
Why?
parentNode.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
if (event.getCode() == KeyCode.A) {
event.consume();
}
});
ah, really strange, seems like KeyEvent.KEY_PRESSED is not sufficient to handle all dispatched events.
If I use the more generic KeyEvent.ANY instead the following code works:
TextField tf = new TextField();
tf.addEventFilter(KeyEvent.ANY, event -> {
if (event.getCharacter().matches("[aA]"))
event.consume();
});

Question about 2 rxjs observables and a timer

I have a question about rxjs in the context of nestjs CQRS sagas.
Suppose I have a scenario where there is two events being published one after another one. One sets the value and the other one unsets it. I need to be able to listen to the event which sets the value for 3 seconds and perform some action if another event has not been published in the meantime.
Here is some code for starters:
valuePersisted = (events$: Observable<any>): Observable<ICommand> => {
return events$
.pipe(
ofType(ValueHasBeenSet),
map(event => {
return new SomeCommand();
}),
);
}
I need to listen to ValueHasBeenUnset event somehow and cancel out of the stream in case this event was received within some time.
EDIT
I just realized that events ValueHasBeenSet and ValueHasBeenUnset can have different value types to be set and unset and code should distinguish that. For example both events have a property called type and its value can be 'blue' | 'yellow'. Is there a way to preserve the logic per event type keeping only two generic events ValueHasBeenSet and ValueHasBeenUnset?
Consider implementing it in the following way:
return events$
.pipe(
ofType(ValueHasBeenSet), // <- listen for ValueHasBeenSet event
switchMap(x => { // <- use switchMap to return a timer
return timer(3000).pipe(
takeUntil(events$.pipe(ofType(ValueHasBeenUnset))), // <- unsubscribe from the timer on ValueHasBeenUnset event
map(event => {
return new SomeCommand(); // <- map to SomeCommand once 3 seconds have passed
})
)
})

RxSwift: how to have one observable trigger another one?

Suppose I have observable A, and I am trying to create observable B that emits two events: the first when A emits an event, and the second 5 seconds later.
So far I have the following:
self.B = Observable.create { [unowned self] observer in
self.A.subscribe(onNext: {
observer.onNext(0)
self.delay(5) {
observer.onNext(1)
}
})
return Disposables.create()
}
This works, but I feel uncomforatble subscribing to A from a closure. Is there a nicer way to do it?
Thanks!
The solution is to reuse the a observable for the delayed observable. Below is the code to do it, along with a proof of concept.
let a = button.rx.tap.asObservable()
let delay = a.delay(5.0, scheduler: MainScheduler.instance)
let b = Observable.of(a, delay).merge()
b.subscribe(onNext: {
print("foo")
}).disposed(by: bag)

Why OnFinished Eventhandler is invoking continuously?

I am new to JavaFX and have a problem with this code
pongAnimation = new Timeline(
new KeyFrame(new Duration(1.0), t -> {
checkForCollision();
})
);
pongAnimation.setCycleCount(Timeline.INDEFINITE);
Why does the Eventhandler
t -> { checkForCollision(); }
start after the Animantion has ended?
We don't have the code
pongAnimation.setOnFinished( t -> {
checkForCollision();)
In my Workbook, it says
When a KeyFrame has an action event handler, the code in that
handler—which in this case is once again a lambda expression—is
executed when the time for that KeyFrame is reached.
My question is when the KeyFrame has an action event handler, why does it start the event again?
Why does the Eventhandler start after the Animantion has ended?
Because you set the animation to repeat indefinitely by
pongAnimation.setCycleCount(Timeline.INDEFINITE);
If you want it to play only once do
pongAnimation.setCycleCount(1);

Resources