4
votes

I'm trying to migrate my FRP understanding from ReactiveCocoa 2.5 to RxSwift and I have one misunderstanding. In ReactiveCocoa, I used rac_signalForSelector when I wanted to observe an invocation of a method. Is there any way to implement this logic using RxSwift?

I wrote a small example in which I want to dispose a subscription when the test method invokes. But in the subscribe block I can still see a next(6) event. What am I doing wrong?

let subject = PublishSubject<Int>()
subject.takeUntil(self.rx.sentMessage(#selector(test))).subscribe { event in
    print(event)
}

subject.onNext(3)
test()
subject.onNext(6)

//////////////////

func test() {

}
5
It's probably because self is non-NSObject. I wouldn't recommend using sentMessage if you can help it. Like you've seen, it can behave strangely and at best, it's a code smell. Use it only when necessary. - solidcell
@solidcell thanks for settings my understanding a bit more! - Nikita Ermolenko

5 Answers

7
votes

You can use sentMessage:

class ViewController: UIViewController {

    let disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.rx.sentMessage(#selector(UIViewController.viewWillAppear(_:)))
            .subscribe({ e in
                print(e)
            })
            .addDisposableTo(disposeBag)
    }
}

Outputs:

next([0])

Or another example:

class SomeNSObjectClass: NSObject {
}

class ViewController: UIViewController {

    let disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()

        let myObj = SomeNSObjectClass()
        myObjc.rx.sentMessage(NSSelectorFromString("dealloc"))
            .subscribe({ e in
                print(e)
            })
            .addDisposableTo(disposeBag)
        }
    }
}

Outputs:

next([])
completed

4
votes

you should use dynamic modifier for test function that access to test function is never inlined or devirtualized by the compiler.

like this: dynamic func test() {}

2
votes

I am adding this comment after encounter the same problem but now I've solved it.

The solution for me, -- I am sorry if this is not the thing you search for but hope will help others with the same issue --, was just adding dynamic modifier for the observed function.

This is the code

func viewDidLoad() {
    rx.sentMessage(#selector(ViewController.test))
        .debug("Test", trimOutput: true)
        .subcribe()
        .disposed(by: bag)
}

@objc dynamic test() {}

Without dynamic modifier the call to test isn't observed because the debug doesn't print anything.

I am new to RxSwift.

Thanks @zhongwuzw above, I got the idea after read his comment.

0
votes

Another solution, which avoids using sentMessage, is to define a testSubject and trigger a next event on it in your test function.

let subject = PublishSubject<Int>()
let testSubject = PublishSubject<Void>()

subject.takeUntil(testSubject).subscribe { event in
    print(event)
}

subject.onNext(3)
test()
subject.onNext(6)

//////////////////

func test() {
    testSubject.onNext(())
}

This will only print until test() is called.

0
votes

RxSwift has Already added this method. look at this issue