Swift 中的Combine 具体使用
本示例基于 Combine 框架实现手机号登录功能,包含手机号输入、验证码输入、协议同意、验证码倒计时、按钮状态绑定以及模拟网络请求。
主要功能点
- 手机号输入:限制11位数字,实时校验有效性。
- 验证码输入:限制4位数字,实时校验有效性。
- 协议开关:用户必须同意协议才能登录。
- 获取验证码按钮:手机号有效且倒计时结束时启用,点击后开始60秒倒计时。
- 登录按钮:手机号有效、验证码有效且协议同意时启用。
- 网络请求模拟:发送验证码和登录请求均模拟异步网络调用,随机成功或失败。
Combine 语法流程图
代码关键点
// 绑定手机号输入限制和发布
NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: phoneNumberField)
.compactMap { ($0.object as? UITextField)?.text }
.map { text -> String in
if text.count > 11 {
return String(text.prefix(11))
}
return text
}
.sink { [weak self] limitedText in
if self?.phoneNumberField.text != limitedText {
self?.phoneNumberField.text = limitedText
}
self?.phone = limitedText
}
.store(in: &subscriptions)
// 手机号有效性校验
var phoneValid: AnyPublisher<Bool, Never> {
$phone
.map { $0?.count == 11 }
.eraseToAnyPublisher()
}
// 验证码有效性校验
var verifyCodeValid: AnyPublisher<Bool, Never> {
$verifyCode
.map { $0?.count == 4 }
.eraseToAnyPublisher()
}
// 按钮状态绑定
Publishers.CombineLatest3(phoneValid, verifyCodeValid, $isAgree)
.map { $0 && $1 && $2 }
.receive(on: RunLoop.main)
.assign(to: .isEnabled, on: logInBtn)
.store(in: &subscriptions)
// 获取验证码按钮状态绑定(需倒计时为0)
phoneValid
.combineLatest($verifyCodeCountdown)
.map { $0 && $1 == 0 }
.receive(on: RunLoop.main)
.assign(to: .isEnabled, on: sendVerifyBtn)
.store(in: &subscriptions)
// 模拟网络请求示例
func requestSendVerifyCode(phone: String) -> AnyPublisher<Bool, Error> {
Future<Bool, Error> { promise in
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
if Int.random(in: 1...100) <= 90 {
promise(.success(true))
} else {
promise(.failure(NetworkError.failedToSendCode))
}
}
}
.receive(on: RunLoop.main)
.eraseToAnyPublisher()
}
该示例清晰展示了如何用 Combine 处理表单输入校验、按钮状态绑定和异步网络请求,适合初学者理解和应用,详情请见demo