根据使用Combine的字符串启用按钮



我有一个名为proceed的登录按钮,并且有两个@Published属性,表示UITextField中当前键入的文本。我想要的是,将.filter { !$0.0.isEmpty && !$0.1.isEmpty}的结果分配给我的UIButtonBool变量isEnabled

目前我的结局是:

passwordTxtf.textView.$text
.combineLatest(loginTxtf.textView.$text)
.filter { !$0.0.isEmpty && !$0.1.isEmpty}
.sink(receiveValue: { [weak self] in
guard let weakSelf = self else { return }
weakSelf.loginEntered = $0
weakSelf.passEntered = $1
weakSelf.setProceedEnableState()
})
.store(in: &subscriptions)

其中setProceedEnableState()是一个检查类变量并因此确定按钮启用状态的函数。

但我想在";管道";,或者以更优雅的方式

您可以构建代码,使textview的每个信号都分配给Subjects。下面是一个基于您的用例的小示例

let isProcessEnabled = PassthroughSubject<Bool, Never>()
let loginEntered = PassthroughSubject<Bool, Never>()
let passEntered = CurrentValueSubject<Bool, Never>(false)
passwordTxtf.textView.$text.map { !$0.isEmpty }
.subscribe(passEntered)
.store(in: &store)
loginTxtf.textView.$text.map { !$0.isEmpty }
.subscribe(loginEntered)
.store(in: &store)
Publishers.CombineLatest(passEntered, loginEntered)
.map { $0 && $1 }
.subscribe(isProcessEnabled)
.store(in: &store)

isProcessEnabled.sink { isEnabled in
print("Is processing enabled", isEnabled)
}.store(in: &store)

这样做的最大优点是可以将isProcessEnabledloginEnteredpassEntered移动到视图模型或其他对象中,这将使整个代码更易于测试。此外,你可以看到,你可以以一些有趣的方式将这些主题中的每一个结合起来,创建一些新的发布者/主题。

class ViewModel {

private var store = Set<AnyCancellable>()
var isProcessingEnabled: AnyPublisher<Bool, Never> {
return processEnabled.eraseToAnyPublisher()
}

var isLoginEntered: AnyPublisher<Bool, Never> {
return loginEntered.eraseToAnyPublisher()
}
var isPassEntered: AnyPublisher<Bool, Never> {
return passwordEntered.eraseToAnyPublisher()
}

func subscribeLoginText(publisher: AnyPublisher<String, Never>) {
publisher.map { !$0.isEmpty }.subscribe(loginEntered).store(in: &store)
}

func subscribePasswordText(publisher: AnyPublisher<String, Never>) {
publisher.map { !$0.isEmpty }.subscribe(passwordEntered).store(in: &store)
}
private var processEnabled = CurrentValueSubject<Bool, Never>(false)
private var loginEntered = PassthroughSubject<Bool, Never>()
private var passwordEntered = PassthroughSubject<Bool, Never>()

init() {
Publishers.CombineLatest(passwordEntered, loginEntered)
.map { $0 && $1 }
.subscribe(processEnabled)
.store(in: &store)
}

}

而且,你可以看到,现在你已经模块化了应用程序,你的视图模型不需要知道你的视图/视图控制器的细节。最重要的是,您的代码现在非常可测试。您可以创建一个视图模型的实例并断言您的假设。

以下是上面视图模型实现的一个小样本测试用例

class ViewModelTest: XCTestCase {

var store: Set<AnyCancellable> = []
var login = PassthroughSubject<String, Never>()
var password = PassthroughSubject<String, Never>()

var isProcessingEnabled = CurrentValueSubject<Bool, Never>(false)
var viewModel: ViewModel = ViewModel()

override func setUp() {
store = []
viewModel = ViewModel()
login = PassthroughSubject<String, Never>()
password = PassthroughSubject<String, Never>()

isProcessingEnabled = CurrentValueSubject<Bool, Never>(false)
viewModel.isProcessingEnabled.subscribe(isProcessingEnabled).store(in: &store)

viewModel.subscribeLoginText(publisher: login.eraseToAnyPublisher())
viewModel.subscribePasswordText(publisher: password.eraseToAnyPublisher())
}

func testThatProcessingIsDisabledInitially() {
XCTAssertFalse(isProcessingEnabled.value, "Processing is enabled")
}

func testThatProcessingIsDisabledWhenOnlyLoginIsEmpty() {
login.send("")
password.send("b")
XCTAssertFalse(isProcessingEnabled.value, "Processing is enabled")
}

func testThatProcessingIsDisabledWhenOnlyPasswordIsEmpty() {
login.send("a")
password.send("")
XCTAssertFalse(isProcessingEnabled.value, "Processing is enabled")

}

func testThatProcessingIsEnabledWhenLoginAndPasswordAreNotEmpty() {
login.send("a")
password.send("b")
XCTAssertTrue(isProcessingEnabled.value, "Processing is disabled")
}
}

相关内容

  • 没有找到相关文章

最新更新