SwiftUI / Combine-订阅单个发布者的最新值

时间:2019-11-02 22:44:48

标签: swift swiftui combine

我正在与Combine和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 TextFields连接到SwiftUI视图。 将为我指出正确的方向提供任何帮助。

1 个答案:

答案 0 :(得分:0)

所以我想出了办法。 在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()的{​​{1}}属性。 更新后的模型如下所示:

deinit