Blazej SLEBODA
Blazej SLEBODA

Reputation: 9925

UITextView publisher doesn't publish a signal when a user taps a keyboard

Apple says:

Combine also provides a built-in publisher for any property that’s compliant with Key-Value Observing.

So, I have an instance of UITextView inside of a view controller view which should emit a signal via publisher when a user taps a keyboard but it doesn't happens. Below a snipet of code which explains my way of subscribing to a publisher

class MyViewController : UIViewController {

    var t = UITextView(frame: .init(x: 0, y: 0, width: 100, height: 20))


    override func viewDidLoad() {
    super.viewDidLoad()

        t.publisher(for: \UITextView.text)
            .receive(on: RunLoop.main)
            .sink { (str) in
                print(str)
        }

        view.addSubview(t)
    }
}

Upvotes: 0

Views: 1135

Answers (2)

Nathan Lawrence
Nathan Lawrence

Reputation: 1

In addition to making sure you store your subscription so it does not get removed by memory management, take heed of the fact that much of UIKit is not KVO-compliant, and that when things do work in KVO, they're typically the result of happy accidents.

This means you probably don't want to rely on KVO to retrieve user input unless you're prepared to continue testing the results of this in a lot of different scenarios for each minor iOS revision.

Instead, you're probably stuck relying on UITextViewDelegate methods like textDidChange(UITextView) for this kind of information — at least for now. I'd be shocked if we didn't have any kind of Combine binding options for UIKit this time next year.

Upvotes: 0

ytyubox
ytyubox

Reputation: 175

You did not store the subscription a.k.a. AnyCancellable. Simply do this:

class MyViewController : UIViewController {

    var t = UITextView(frame: .init(x: 0, y: 0, width: 100, height: 20))
    var set = Set<AnyCancellable>()

    override func viewDidLoad() {
    super.viewDidLoad()

        t.publisher(for: \UITextView.text)
            .receive(on: RunLoop.main)
            .sink { (str) in
                print(str)
        }.store(in: &set)

        view.addSubview(t)
    }
}

Upvotes: 1

Related Questions