SwiftUI 和 Combine,如何创建可重用的发布者来检查字符串是否为空

SwiftUI and Combine, how to create a reusable publisher to check if a string is empty

我正在努力学习 SwiftUI 和 Combine 语法,并试图了解如何创建一个可重用的发布者来检查字符串是否为空。

我有一个 SwiftUI,它有 5 个文本字段,使用 @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/,并且我能够通过创建 [=13] 让我的 SwiftUI 正确地 enable/disable 我的计算按钮=] 和 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 语法时遇到了很多麻烦,我似乎无法弄清楚如何去做,每个例子我在网络上查找只是内联定义发布者链,而不是以可重用的方式。

有什么帮助吗?我怎样才能创建一个可重用的发布者,这样我就不必重复这些都做同样事情的内联发布者?

给你!

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

[=12=] 是闭包的第一个参数 shorthand,</code> 表示第二个,依此类推。</p> <p><code>!Bool反转运算符,前缀!是shorthand后缀== 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 是泛型,这意味着它可以是任何类型,只要该类型符合 Publisherwhere 子句允许我们进一步限制这种一致性,表示我们只能在 OutputString 时对 Publisher 进行操作。 some Publisher 为我们提供了一个不透明的 return 类型,使我们不必编写已多次转换的 Publisher 的类型签名,您可以将其更改为 AnyPublisher<Bool, Never> 和如果愿意,可以使用 .eraseToAnyPublisher(),但我建议仅在需要时使用该擦除。