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

标签 swift swiftui combine

我正在为我的一个小宠物项目尝试一些 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/

相关文章:

swift - 错误 "Value of type ' String' has no member 'URLbyAppending' "with my Record Function

swift - 如何将多个 SCNScene 添加到一个 SCNScene?

ios - 为什么在更改 View 的 id 时不会第二次调用 onAppear()?

ios - 组合框架更新 UI 无法正常工作

swift - 如何使用Swinject注册符合ObservableObject的协议(protocol)?

ios - AWSMobileHub 使用 Swift 在 IOS 中缓存来自 S3 的图像

swift - Swift 中的 secp256k1 公钥解析

swift - Swift 3 中 iOS 9 的核心数据不起作用

ios - 如何使用 ViewModifier 在 swiftUI 中选择性地设置前景色以查看

swift - 有没有一种组合方法可以阻止多个请求被触发?