首页 > 解决方案 > SwiftUI和Combine,如何创建一个可重用的发布者来检查一个字符串是否为空

问题描述

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

我有一个带有 5 个 TextField 的 SwiftUI,它使用 @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 语法时遇到了很多麻烦,而且我似乎无法弄清楚如何去做,而且我在网上找到的每个示例都只是定义发布者链内联而不是以可重用的方式。

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

标签: swiftswiftuicombine

解决方案


给你!

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

$0是闭包的第一个参数的简写,$1表示第二个参数,依此类推。

!Bool反转运算符,prefixing!是 suffixing 的简写== 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是泛型,这意味着它可以是任何类型,只要该类型符合Publisher. 该where子句允许我们进一步限制这种一致性,表示我们只能在它们是Publisher时对 s 进行操作。为我们提供了一个不透明的返回类型,以使我们不必编写已多次转换的 a 的类型签名,您可以将其更改为并根据需要使用,但我建议仅在需要时使用该擦除。OutputStringsome PublisherPublisherAnyPublisher<Bool, Never>.eraseToAnyPublisher()


推荐阅读