Combine: how to collapse the publisher type?

How can I collapse a publisher's type that was built using few operators? Say I have a UITextField and want a publisher that signals whether that text field has value of at least N characters in length. I could build that publisher like this:

let textFieldValuePublisher = textField.publisher(for: \.text)
let valueLengthPublisher = textFieldValuePublisher.map { $0?.lengthInBytes(using: .utf8) ?? 0 }
let minimumLengthPredicatePublisher = valueLengthPublisher.map { $0 > N }

The type of minimumLengthPredicatePublisher has a lot of nested types in generics. Is there a way to get a Publisher<Bool, Never> from it?

Use the eraseToAnyPublisher() at the end to get an AnyPublisher<Bool, Never>.

However, I don't know if UITextField's text property is KVO-compliant. It's not documented as such.

1 Like

I tried using reaseToAnyPublisher(), however when inspecting the resulting publisher Xcode shows something along the lines of AnyPublisher<(loads of nexted generics).Output, Never>. :(

Yes but it's the same as AnyPublisher<Bool, Never>.

1 Like

I wonder where I am mistaken when I try to subscribe an Subscriber.Assign<UIButton, Bool>? I get the compilation error here:

let boolSubscriber = Subscribers.Assign(object: button, keyPath: \.isEnabled)

publisher.subscribe(boolSubscriber) // No exact matches in call to instance method 'subscribe'

Without seeing all the relevant code, I can't say what you've done wrong. This compiles:

import UIKit
import Combine

let textField = UITextField()
let button = UIButton()

let boolPublisher: AnyPublisher<Bool, Never> =
    textField.publisher(for: \.text)
    .map { ($0 ?? "").count > 5 }
    .eraseToAnyPublisher()

let assigner = Subscribers.Assign(object: button, keyPath: \.isEnabled)
boolPublisher.subscribe(assigner)

But creating an Assign explicitly is unusual. You probably want something more like this:

import UIKit
import Combine

class MyController: UIViewController {
    let textField = UITextField()
    let button = UIButton()

    private var tickets: [AnyCancellable] = []

    override func viewDidLoad() {
        super.viewDidLoad()

        textField.publisher(for: \.text)
            .map { ($0 ?? "").count > 5 }
            .assign(to: \.isEnabled, on: button)
            .store(in: &tickets)
    }
}

But again I must emphasize that the text property on UITextField is not documented to support Key-Value Observing. Therefore textField.publisher(for: \.text) might not work reliably.

1 Like

The missing piece was explicit type declaration for the final publisher. Thanks a heap!

Terms of Service

Privacy Policy

Cookie Policy