我正在为我的一个小宠物项目尝试一些 Joint 和 SwiftUI,边学边做。
这是当前状态的 LoginModel
:
public class LoginModel: ObservableObject {
@Published var domain: String = ""
@Published var email: String = ""
@Published var password: String = ""
@Published var isValid: Bool = false
public var didChange = PassthroughSubject<Void, Never>()
var credentialsValidPublisher: AnyPublisher<Bool, Never> {
Publishers.CombineLatest($email, $password)
.receive(on: RunLoop.main)
.map { (email, password) in
let emailValid = String.emailValid(emailString: email) // String extension function
let passwordValid = password.count > 5
return emailValid && passwordValid
}
.breakpointOnError()
.eraseToAnyPublisher()
}
init() {
// This works just fine
_ = credentialsValidPublisher.sink { isValid in
self.isValid = isValid
}
// However this does not work at all
_ = domain
.publisher
.receive(on: RunLoop.main)
.sink { value in
print(value)
}
}
}
现在,根据我目前的理解,@Published var foo: String
已经附加了一个Publisher
。并且应该能够直接使用它来订阅其更改。
将 credentialsValidPublisher
变量更改为此也可以:
var credentialsValidPublisher: AnyPublisher<Bool, Never> {
Publishers.CombineLatest3($domain, $email, $password)
.receive(on: RunLoop.main)
.map { (domain, email, password) in
let domainValid = URL.isValidURL(urlString: domain)
let emailValid = String.emailValid(emailString: email)
let passwordValid = password.count > 5
return domainValid && emailValid && passwordValid
}
.breakpointOnError()
.eraseToAnyPublisher()
}
但这不是我想要的。就我而言,我需要一个特殊的 Publisher
来将有效的 URL 字符串映射到网络请求,然后 ping 手边的服务器以查看提供的服务器是否正在响应。
此外,该模型还连接到带有一堆 SwiftUI TextField 的 SwiftUI View 。 任何帮助我指明正确方向的帮助都将受到高度赞赏。
最佳答案
所以我想出了办法。
在 LoginModel
下面var credentialsValidPublisher: AnyPublisher<Bool, Never>
我补充道:
var domainValidPublisher: AnyPublisher<Bool, Never> {
$domain
.debounce(for: 0.5, scheduler: DispatchQueue.main)
.removeDuplicates()
.map { domain in
URL.isValidURL(urlString: domain)
}
.eraseToAnyPublisher()
}
然后我就可以订阅init
。
我还添加了AnyCancellable
我们称之为 .cancel()
的属性上deinit
。
更新后的模型如下所示:
public class LoginModel: ObservableObject {
@Published var domain: String = ""
@Published var email: String = ""
@Published var password: String = ""
@Published var isValid: Bool = false
public var didChange = PassthroughSubject<Void, Never>()
private var credentialsValidPublisherCancellable: AnyCancellable!
private var domainValidCancellable: AnyCancellable!
var credentialsValidPublisher: AnyPublisher<Bool, Never> {
Publishers.CombineLatest3($domain, $email, $password)
.receive(on: RunLoop.main)
.map { (domain, email, password) in
let domainValid = URL.isValidURL(urlString: domain)
let emailValid = String.emailValid(emailString: email)
let passwordValid = password.count > 5
return domainValid && emailValid && passwordValid
}
.breakpointOnError()
.eraseToAnyPublisher()
}
var domainValidPublisher: AnyPublisher<Bool, Never> {
$domain
.debounce(for: 0.5, scheduler: DispatchQueue.main)
.removeDuplicates()
.map { domain in
URL.isValidURL(urlString: domain)
}
.eraseToAnyPublisher()
}
init() {
credentialsValidPublisherCancellable = credentialsValidPublisher.sink { isValid in
self.isValid = isValid
}
domainValidCancellable = domainValidPublisher.sink { isValid in
print("isValid: \(isValid)")
}
}
deinit {
credentialsValidPublisherCancellable.cancel()
domainValidCancellable.cancel()
}
}
关于SwiftUI/Combine - 订阅单个发布者的最新值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58675933/