SwiftUI и Combine, как создать многоразового издателя, чтобы проверить, пуста ли строка - PullRequest
2 голосов
/ 02 апреля 2020

Я изо всех сил пытаюсь изучить синтаксис SwiftUI и Combine и пытаюсь понять, как создать повторно используемого издателя, который будет проверять, является ли строка пустой.

У меня есть SwiftUI с 5 TextFields, которые используют @Binding для подключения их к моему объекту модели данных.

class DataWhatIsLoanPayment: ObservableObject {
    // Input
    @Published var pv = ""
    @Published var iyr = ""
    // a bunch more fields...

    // Output
    @Published var isvalidform = false
}

Я хочу включить кнопку «Рассчитать» после заполнения всех полей (isEmpty == false).

Я следую вместе с https://peterfriese.dev/swift-combine-love/, и я смог заставить свой SwiftUI правильно включить / отключить кнопку «Рассчитать», создав isValidPVPublisher и isValidIYRPublisher и объединяя их в isValidFormPublisher, например так:

private var isValidPVPublisher: AnyPublisher<Bool, Never> {
    $pv
        .debounce(for: 0.8, scheduler: RunLoop.main)
        .removeDuplicates()
        .map { input in
            return input.isEmpty == false
        }
        .eraseToAnyPublisher()
}

private var isValidIYRPublisher: AnyPublisher<Bool, Never> {
    $iyr
        .debounce(for: 0.8, scheduler: RunLoop.main)
        .removeDuplicates()
        .map { input in
            return input.isEmpty == false
        }
        .eraseToAnyPublisher()
}


private var isValidFormPublisher: AnyPublisher<Bool, Never> {
    Publishers.CombineLatest(isValidPVPublisher, isValidIYRPublisher)
        .map { pvIsValid, iyrIsValid in
            return pvIsValid && iyrIsValid
        }
        .eraseToAnyPublisher()
}

init() {        
    isValidFormPublisher
        .receive(on: RunLoop.main)
        .assign(to: \.isValidForm, on: self)
        .store(in: &cancellableSet)
}

Однако у меня будет намного больше, чем 2 поля, и в моем приложении будет много других форм. в котором я хочу проверить, если мои поля пусты. И повторять .debounce(for: 0.8, scheduler: RunLoop.main).removeDuplicates().map { input in return input.isEmpty == false }.eraseToAnyPublisher() снова и снова - плохая идея.

Я хочу создать повторно используемую NotEmptyPublisher, или что-то в этом роде, которая берет привязку к полю, как мой $pv, и устанавливает цепочку, как показано в isValidPVPublisher выше. Таким образом, у меня может быть что-то вроде:

// Something like this, but I'm not sure of the syntax...
private var isValidPVPublisher = NotEmptyPublisher(field:$pv)
// instead of ...
private var isValidPVPublisher: AnyPublisher<Bool, Never> {
    $pv
        .debounce(for: 0.8, scheduler: RunLoop.main)
        .removeDuplicates()
        .map { input in
            return input.isEmpty == false
        }
        .eraseToAnyPublisher()
}

Но у меня большие проблемы с синтаксическим анализом большого количества синтаксиса Swift, с которым я не знаком и не могу понять, как сделайте это, и каждый пример, который я нахожу в Интернете, просто определяет цепочку издателя, а не используется повторно.

Любая помощь? Как я могу создать многоразового издателя, чтобы мне не приходилось повторять этих встроенных издателей, которые все делают одно и то же?

1 Ответ

2 голосов
/ 02 апреля 2020

Вот, пожалуйста!

extension Publisher where Output == String {
    func isStringInhabited() -> Publishers.Map<Self, Bool> {
        map { !$0.isEmpty }
    }
}

$0 - это сокращение от первого аргумента до закрытия, $1 означает второй и т. Д. И т. Д.

! - это оператор инверсии Bool, префикс ! - сокращение от суффикса == false.

Теперь, что касается вашего вопроса о повторном использовании, вам не нужно слишком сильно перебивать вещи, вы можете просто создайте функцию.

private func isValidTransform<P: Publisher>(input: P) -> some Publisher where P.Output == String {
    input
        .debounce(for: 0.8, scheduler: RunLoop.main)
        .removeDuplicates()
        .isStringInhabited()
}

P - это шаблон c, что означает, что это может быть любой тип, если этот тип соответствует Publisher. Предложение where позволяет нам еще больше ограничить это соответствие, указывая, что мы можем работать только с Publisher с, когда их Output равно String. some Publisher дает нам непрозрачный тип возвращаемого значения, чтобы избавить нас от необходимости писать сигнатуру типа Publisher, который был преобразован несколько раз, вы можете изменить его на AnyPublisher<Bool, Never> и использовать .eraseToAnyPublisher(), если хотите, но я рекомендую использовать только это стирание во время необходимости.

...