登录

BindTo方式

手机号输入页面
  • 页面原型
  • 需求设计
 1、手机号输入11位时,按钮【下一步】可点击,否则灰显;

 2、点击【下一步】校验输入字符是否为手机号,正确走3,否则校验失败,toast提示;

 3、点击【下一步】手机号校验通过后,调用校验手机号是否已经注册API,“是”则调入【密码页面】,“否”调入【注册页面】。
  • 代码实现
model : 负责定义数据结构
struct InputMobileResult: Mapable {
    var code: String?
    var message: String?

    init() {

    }

    init?(json: JSON) {
        code = json["code"].stringValue
        message = json["message"].stringValue
    }
}
service : 负责常规校验(字段、合法性等)和API调用
class AccountService {

    static let instance = AccountService()
    let disposeBag = DisposeBag()

    private init() {}

    // MARK: - 手机号码合法性校验
    func validateMobile(_ mobile: String) -> CommonResult {
        if mobile.count == 11 && mobile.td.isPhoneNumber() {
            return .success(message:"")
        } else if mobile.count > 11 {
            return .failed(message:"")
        } else {
            return .empty
        }
    }

    // MARK: - 校验手机号码是否已经注册
    func mobileNextStep(_ mobile: String) -> Observable<InputMobileResult?> {
        return APIService.rx.request(MultiTarget(UserRequest.uservalidateTelNo(tel: mobile)))
            .asObservable()
            .debug("Observable")
            .mapResponseToObj(type: InputMobileResult.self)
    }

    // MARK: - 校验登录密码
    func validatePassword(_ password: String) -> Observable<CommonResult> {
        if !password.isEmpty {
            return .just(.success(message:""))
        } else {
            return .just(.failed(message:""))
        }
    }

    // 登录
    func passwordNextStep(_ mobile: String, password: String) -> Driver<InputPasswordResult?> {
        return APIService.rx.request(MultiTarget(UserRequest.userlogin(imgCode: "", password: password, telno: mobile)))
            .asObservable()
            .debug("Observable")
            .mapResponseToObj(type: InputPasswordResult.self)
            .asDriver(onErrorJustReturn: InputPasswordResult(json: ["code": "-1", "message": String.td.localized("TD_Empty_Data_InternetTitle")]))
    }
}
viewModel: 负责与controller通信
class InputMobileViewModel: MVVMTDPInputMobileViewModel {

    let service = AccountService.instance

    // input
    let mobile = Variable<String>("")         // 手机号
    let nextTaps = PublishSubject<Void>()     // 按钮点击

    // output
    let mobileUsable: Observable<CommonResult>       // 手机号合法性校验结果
    let nextButtonEnabled: Observable<Bool>          // 按钮可点击状态
    let nextResult: Observable<InputMobileResult?>   // 手机号API校验结果

    init() {
        let service = AccountService.instance

        // 手机号合法性校验结果
        mobileUsable = mobile.asObservable()
            .map { mobile in
                return service.validateMobile(mobile)
            }
            .share(replay: 1)

        // 按钮可点击状态
        nextButtonEnabled = mobileUsable.asObservable()
            .map({ (mobile) in
                return mobile.isValid
            })
            .distinctUntilChanged()
            .share(replay: 1)

        let paramters = Observable.combineLatest(mobile.asObservable(), mobile.asObservable()) {
            ($0, $1)
        }

        // 手机号API校验结果
        nextResult = nextTaps.asObservable().withLatestFrom(paramters)
            .flatMapLatest { (mobile, _) in
                return service.mobileNextStep(mobile)
                    .observeOn(MainScheduler.instance)
                    .catchErrorJustReturn(InputMobileResult(json: ["code": "-1", "message": String.td.localized("TD_Empty_Data_InternetTitle")]))
            }
            .share(replay: 1)
    }
}
controller:负责业务的组装
    let disposeBag = DisposeBag()
    let service = AccountService.instance

    // 属性绑定Rx
    func rxSwiftBindTo() {
        let inputViewModel = InputMobileViewModel()

        // inputViewModel.mobile监听mobileTextField.text
        mobileTextField.rx.text.orEmpty
            .bind(to: inputViewModel.mobile)
            .disposed(by: disposeBag)

        // inputViewModel.nextTaps监听nextBtn.tap
        nextBtn.rx.tap
            .bind(to: inputViewModel.nextTaps)
            .disposed(by: disposeBag)

        // inputViewModel.nextButtonEnabled 关联事件
        inputViewModel.nextButtonEnabled
            .subscribe(onNext: { [unowned self] valid in
                self.nextBtn.isEnabled = valid
                self.nextBtn.isUserInteractionEnabled = valid
                self.nextBtn.backgroundColor = valid ? UIColor(valueRGB: 0xc8a66b) :  UIColor(valueRGB: 0xc8a66b, alpha: 0.3)
            })
            .disposed(by: disposeBag)

        // 点击【下一步】API校验回调结果处理
        inputViewModel.nextResult.subscribe(onNext: { [unowned self] result in
            self.hideHub(title: nil)
            if result?.code == "1" {
                self.gotoInputPassword(phone: (self.mobileTextField.text!)) // 输入密码
            } else if result?.code == "0" {
                self.gotoRegister(phone: (self.mobileTextField.text!))      // 调入注册
            } else {
                self.errorMessageDidChange(message: (result?.message ?? "").isEmpty ? "验证失败": (result?.message!)!)
            }
        }).disposed(by: disposeBag)
    }

Driver方式

密码输入页面
model : 负责定义数据结构
struct UserLoginModelInfo: Mapable {
    var headImage: String?
    var telno: String?
    var token: String?
    var uname: String?
    var id: Int?

    init?(json: JSON) {
        headImage = json["headImage"].stringValue
        id = json["id"].intValue
        telno = json["telno"].stringValue
        token = json["token"].stringValue
        uname = json["uname"].stringValue
    }
}

struct InputPasswordResult: Mapable {
    var data: UserLoginModelInfo?
    var message: String?
    var code: String?

    init() {

    }

    init?(json: JSON) {
        code = json["code"].stringValue
        message = json["message"].stringValue

        if json["data"] != JSON.null {
            data = UserLoginModelInfo.init(json: json["data"])
        }
    }
}
service : 负责常规校验(字段、合法性等)和API调用
class AccountService {

    static let instance = AccountService()
    let disposeBag = DisposeBag()

    private init() {}

    // MARK: - 校验登录密码
    func validatePassword(_ password: String) -> Observable<CommonResult> {
        if !password.isEmpty {
            return .just(.success(message:""))
        } else {
            return .just(.failed(message:""))
        }
    }

    // 登录
    func passwordNextStep(_ mobile: String, password: String) -> Driver<InputPasswordResult?> {
        return APIService.rx.request(MultiTarget(UserRequest.userlogin(imgCode: "", password: password, telno: mobile)))
            .asObservable()
            .debug("Observable")
            .mapResponseToObj(type: InputPasswordResult.self)
            .asDriver(onErrorJustReturn: InputPasswordResult(json: ["code": "-1", "message": String.td.localized("TD_Empty_Data_InternetTitle")]))
    }
}
viewModel: 负责与controller通信
class InputPasswordViewModel {

    let loginButtonEnabled: Driver<Bool>

    init(_ password: Driver<String>) {
        loginButtonEnabled = password
            .map { !$0.isEmpty }
            .asDriver()
    }
}
controller:负责业务的组装
    // MARK: - const
    let dispose = DisposeBag()
    let service = AccountService.instance

    // 使用Driver绑定
    func driverTo() {
        let passwordViewModel = InputPasswordViewModel(passwordTextField.rx.text.orEmpty.asDriver())

        passwordViewModel.loginButtonEnabled.drive(onNext: { [unowned self] valid in
            self.loginBtn.isEnabled = valid
            self.loginBtn.isUserInteractionEnabled = valid
            self.loginBtn.backgroundColor =  valid ? UIColor(valueRGB: 0xc8a66b) :  UIColor(valueRGB: 0xc8a66b, alpha: 0.3)
        })
        .disposed(by: disposeBag)
    }

    // 登录
    func login() {
        let mobile = self.getExtra("phone") as! String
        self.showHub(title: nil)
        self.service.passwordNextStep(mobile, password: self.passwordTextField.text!)
            .drive(onNext: { [unowned self] result in
                self.hideHub(title: nil)
                if result?.code == "1"{
                    self.service.localizationUserInfo(mobile, model: result!)
                    // TDO 业务逻辑
                } else {
                    self.errorMessageDidChange(message: (result?.message ?? "").isEmpty ? String.td.localized("Login_Fail") : (result?.message!)!)
                }
            })
            .disposed(by: disposeBag)
    }

注册

BindTo方式

model : 负责定义数据结构
struct UserRegisterModelResult: Mapable {
    var data: UserRegisterModelData?
    var message: String?
    var code: String?
    var uname: String?
    var id: Int?

    init() {

    }

    init?(json: JSON) {
        code = json["code"].stringValue
        message = json["message"].stringValue
        uname = json["uname"].stringValue
        id = json["id"].intValue

        if json["data"] != JSON.null {
            data = UserRegisterModelData(json: json["data"])
        }
    }
}
service : 负责常规校验(字段、合法性等)和API调用
// MARK: - 校验验证码
    func validateVerifyCode(_ code: String) -> CommonResult {
        if !code.isEmpty {
            return .success(message: "")
        } else {
            return .failed(message: "")
        }
    }

    // MARK: - 校验注册密码
    func validatePassword(_ password: String) -> CommonResult {
        if password.count >= 6 &&  password.count <= 16 {
            return .success(message: "")
        } else {
            return .failed(message: "")
        }
    }

    // 用户注册
    func regsiter(_ mobile: String, vertifyCode: String, password: String) -> Observable<UserRegisterModelResult?> {
        return APIService.rx.request(MultiTarget(UserRequest.registerup(tel: mobile, password: password, telCode: vertifyCode, agree: "1")))
            .asObservable()
            .debug("Observable")
            .mapResponseToObj(type: UserRegisterModelResult.self)
    }
viewModel: 负责与controller通信
class RegisterViewModel: MVVMTDRegisterViewModel {

    // input
    let mobile = Variable<String>("")
    let mobileCode = Variable<String>("")
    let loginPwd = Variable<String>("")
    let registerTaps = PublishSubject<Void>()
    let sumbitDataEvent = PublishSubject<Void>()

    //output
    let mobileCodeUsable: Observable<CommonResult>
    let passwordUsable: Observable<CommonResult>
    let registerButtonEnabled: Observable<Bool>
    let registerResult: Observable<UserRegisterModelResult?>

    init() {

        let service = AccountService.instance

        mobileCodeUsable = mobileCode.asObservable().map({ (code) in
            return service.validateVerifyCode(code)
        })
        .share(replay: 1)

        passwordUsable = loginPwd.asObservable().map({ (password) in
            return service.validatePassword(password)
        })
        .share(replay: 1)

        registerButtonEnabled = Observable.combineLatest(mobileCodeUsable, passwordUsable) { (mobileCode, loginPwd) in
            return mobileCode.isValid && loginPwd.isValid
            }
            .distinctUntilChanged()
            .share(replay: 1)

        let mobileAndPassword = Observable.combineLatest(mobile.asObservable(), mobileCode.asObservable(), loginPwd.asObservable()) {
            ($0, $1, $2)
        }

        registerResult = registerTaps.asObservable().withLatestFrom(mobileAndPassword)
            .flatMapLatest { (mobile, mobileCode, loginPwd) in
                return service.regsiter(mobile, vertifyCode: mobileCode, password: loginPwd)
                    .observeOn(MainScheduler.instance)
                    .catchErrorJustReturn(UserRegisterModelResult(json: ["code": "-1", "message": String.td.localized("TD_Empty_Data_InternetTitle")]))
            }
            .share(replay: 1)
    }
}
controller:负责业务的组装
    // MARK: - const
    let dispose = DisposeBag()
    let service = AccountService.instance
    let rxViewModel = RegisterViewModel()

    func rxSwiftBindTo() {
        let mobile = Variable<String>(self.getExtra("phone") as! String)

        mobile.asObservable()
            .bind(to: rxViewModel.mobile)
            .disposed(by: dispose)

        bgView.msgVerifyCodeTextField.rx.text.orEmpty
            .bind(to: rxViewModel.mobileCode)
            .disposed(by: dispose)

        bgView.loginPwdTextField.rx.text.orEmpty
            .bind(to: rxViewModel.loginPwd)
            .disposed(by: dispose)

        bgView.regBtn.rx.tap
            .bind(to: rxViewModel.registerTaps)
            .disposed(by: dispose)

        rxViewModel.registerButtonEnabled.subscribe(onNext: { [unowned self] valid in
            self.bgView.regBtn.isEnabled = valid
            self.bgView.regBtn.isUserInteractionEnabled = valid
            self.bgView.regBtn.alpha = valid ? 1.0 : 0.5
            self.bgView.regBtn.backgroundColor =  valid ? UIColor(valueRGB: 0xc8a66b) :  UIColor(valueRGB: 0xc8a66b, alpha: 0.3)
        })
            .disposed(by: dispose)

        rxViewModel.registerResult
            .subscribe(onNext: { [unowned self] result in
                self.hideHub(title: nil)
                if result?.code == "1" {
                    self.service.regsiterSuccess(mobile: mobile.value, model: result!)
                    self.didRegister()
                } else {
                    self.errorMessageDidChange(message: (result?.message)!)
                }
            })
            .disposed(by: dispose)

        // 先创建一个subscribe,需要的时候使用onNext()
        rxViewModel.sumbitDataEvent
            .flatMapLatest { _ in
                self.service.regsiter((self.viewModel?.phone)!,
                                      vertifyCode: self.bgView.msgVerifyCodeTextField.text!,
                                      password: self.bgView.loginPwdTextField.text!)
            }
            .subscribe(onNext: { [unowned self] result in
                self.hideHub(title: nil)
                if result?.code == "1" {
                    self.service.regsiterSuccess(mobile: mobile.value, model: result!)
                    self.didRegister()
                } else {
                    self.errorMessageDidChange(message: (result?.message)!)
                }
            })
            .disposed(by: dispose)
    }

results matching ""

    No results matching ""