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

Ken*_*and 3 swift 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
}
Run Code Online (Sandbox Code Playgroud)

我想在填写所有字段后启用“计算”​​按钮(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)
}
Run Code Online (Sandbox Code Playgroud)

但是,我将有 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()
}
Run Code Online (Sandbox Code Playgroud)

但是我在解析很多我不熟悉的 Swift 语法时遇到了很多麻烦,而且我似乎无法弄清楚如何去做,而且我在网上找到的每个例子都只是定义了发布者链内联而不是以可重用的方式。

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

Lot*_*lla 5

这个给你!

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

$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()
}
Run Code Online (Sandbox Code Playgroud)

P是泛型,这意味着它可以是任何类型,只要该类型符合Publisher. 该where子句允许我们进一步限制这种一致性,表示我们只能在Publishers 时对其进行Output操作Stringsome Publisher给了我们一个不透明的返回类型,使我们不必编写Publisher已经多次转换的类型签名,您可以将其更改为AnyPublisher<Bool, Never>并使用,.eraseToAnyPublisher()如果您愿意,但我建议仅在需要时使用该擦除。