我正在努力学习SwiftUI和Combine语法,并试图了解如何创建一个可重复使用的发布服务器,以检查String是否为空。
我有一个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/,并且我能够通过创建isValidPVPublisher
和isValidIYRPublisher
并将它们组合到isValidFormPublisher
中来使我的SwiftUI正确地启用/禁用我的计算按钮,如下所示:
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 { !$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
是泛型,这意味着它可以是任何类型,只要该类型符合Publisher
。where
子句允许我们进一步约束这种一致性,表示只有当Output
是String
时,我们才能对Publisher
进行操作。some Publisher
为我们提供了一个不透明的返回类型,使我们不必编写经过多次转换的Publisher
的类型签名,如果您愿意,您可以将其更改为AnyPublisher<Bool, Never>
并使用.eraseToAnyPublisher()
,但我建议仅在需要时使用该擦除。