美文网首页
RxSwift 案例学习(一)

RxSwift 案例学习(一)

作者: 狂奔的兔子 | 来源:发表于2017-02-07 09:33 被阅读959次

    本文是官方案例GitHubSignup-UsingDriver学习笔记

    项目实现功能

    这个登录页面实现了下面几个功能:
    1.检验用户名是否可用
    2.密码是否符合要求
    3.确认密码是符合密码一样
    4.上面上面三个都符合要求,登录按钮才可以点击
    5.当用户正在登录的时候,显示 activityIndicator,提醒用户等待,此时按钮不能被按;当得到登录结果的时候,隐藏 activityIndicator。
    6.登录完成显示登录结果

    具体实现

    GitHubSignupViewController2.swift

    #if !RX_NO_MODULE
    import RxSwift
    import RxCocoa
    #endif
    

    引用部分RX_NO_MODULE这个宏的字面意思应该是没有rx的模块意思,但是没有找到具体实现在哪里,如果谁知道麻烦告知

        @IBOutlet weak var usernameOutlet: UITextField!
        @IBOutlet weak var usernameValidationOutlet: UILabel!
    
        @IBOutlet weak var passwordOutlet: UITextField!
        @IBOutlet weak var passwordValidationOutlet: UILabel!
        
        @IBOutlet weak var repeatedPasswordOutlet: UITextField!
        @IBOutlet weak var repeatedPasswordValidationOutlet: UILabel!
        
        @IBOutlet weak var signupOutlet: UIButton!
        @IBOutlet weak var signingUpOulet: UIActivityIndicatorView!
    

    首先是各个控件的绑定

     let viewModel = GithubSignupViewModel2(
                input: (
                    username: usernameOutlet.rx.text.orEmpty.asDriver(),
                    password: passwordOutlet.rx.text.orEmpty.asDriver(),
                    repeatedPassword: repeatedPasswordOutlet.rx.text.orEmpty.asDriver(),
                    loginTaps: signupOutlet.rx.tap.asDriver()
                ),
                dependency: (
                    API: GitHubDefaultAPI.sharedAPI,
                    validationService: GitHubDefaultValidationService.sharedValidationService,
                    wireframe: DefaultWireframe.sharedInstance
                )
            )
    

    viewModel初始化,rx是RxSwift的域名,text是观察的属性,orEmpty是检验text是否为nil如果为nil返回"",asDriver()是具体特殊属性的Observable,如果使用asObservable(),就要额外添加.observeOn(MainScheduler.instance)和.shareReplay(1),
    Driver是属于Rxcocoa库,是对Observable进行了一些封装,Observable是属于RxSwift库

      viewModel.signupEnabled
                .drive(onNext: { [weak self] valid  in
                    self?.signupOutlet.isEnabled = valid
                    self?.signupOutlet.alpha = valid ? 1.0 : 0.5
                })
                .addDisposableTo(disposeBag)
    

    监听viewModel.signupEnabled的值变化,signupEnabled的类型为
    Driver<Bool>,那么valid的类型就为Bool,根据valid来设置按钮的状态

      viewModel.validatedUsername
                .drive(usernameValidationOutlet.rx.validationResult)
                .addDisposableTo(disposeBag)
    
      viewModel.validatedPassword
                .drive(passwordValidationOutlet.rx.validationResult)
                .addDisposableTo(disposeBag)
    
    viewModel.validatedPasswordRepeated
              .drive(repeatedPasswordValidationOutlet.rx.validationResult)
                .addDisposableTo(disposeBag)
    
    viewModel.signingIn
                .drive(signingUpOulet.rx.isAnimating)
                .addDisposableTo(disposeBag)
    
    

    将viewModel.validatedUsername和UILabel的validationResult绑定, validationResult是UILabel自定义的Rx扩展,源码如下:

    extension Reactive where Base: UILabel {
        var validationResult: UIBindingObserver<Base, ValidationResult> {
            return UIBindingObserver(UIElement: base) { label, result in
                label.textColor = result.textColor
                label.text = result.description
            }
        }
    }
    
       let tapBackground = UITapGestureRecognizer()
            tapBackground.rx.event
                .subscribe(onNext: { [weak self] _ in
                    self?.view.endEditing(true)
                })
                .addDisposableTo(disposeBag)
            view.addGestureRecognizer(tapBackground)
    

    以上是新建一个tap手势并使用Rx对手势的监听来实现相应的功能

    GithubSignupViewModel2.swift

       init(
            input: (
                username: Driver<String>,
                password: Driver<String>,
                repeatedPassword: Driver<String>,
                loginTaps: Driver<Void>
            ),
            dependency: (
                API: GitHubAPI,
                validationService: GitHubValidationService,
                wireframe: Wireframe
            )
        )
    

    首先是初始化接受一个两个元组作为参数

     validatedUsername = input.username
                .distinctUntilChanged() //demo重复检查
                .flatMapLatest { username in
                    return validationService.validateUsername(username)
                        .asDriver(onErrorJustReturn: .failed(message: "Error contacting server"))
                }
    
     validatedPassword = input.password
                .map { password in
                    return validationService.validatePassword(password)
                }
    

    对username和password的值进行处理,然后返回Driver<ValidationResult>的结果为后面处理做准备

    validatedPasswordRepeated = Driver.combineLatest(input.password, input.repeatedPassword, resultSelector: validationService.validateRepeatedPassword)
    

    Driver.combineLatest将两个流合并成一个流,通过对源码的阅读发现combineLatest最多支持8路流合并成一路流, resultSelector提供多路流合并的方法,这里可以写成闭包的形式,也可以直接传入一个处理函数.

    let signingIn = ActivityIndicator()
    

    ActivityIndicator提供检测网络访问状态的方法

    self.signingIn = signingIn.asDriver()
    

    提供网络访问的状态监听

    .trackActivity(signingIn)
    

    在网络请求时添加到上面的方法,可以监听网络状态,网络开始访问时返回true,网络访问结束时返回false

    signedIn = input.loginTaps.withLatestFrom(usernameAndPassword)
                .flatMapLatest { (username, password) in
                    return API.signup(username, password: password)
                        .trackActivity(signingIn)
                        .asDriver(onErrorJustReturn: false)
                }
                .flatMapLatest { loggedIn -> Driver<Bool> in
                    let message = loggedIn ? "Mock: Signed in to GitHub." : "Mock: Sign in to GitHub failed"
                    return wireframe.promptFor(message, cancelAction: "OK", actions: [])
                        // propagate original value
                        .map { _ in
                            loggedIn
                        }
                        .asDriver(onErrorJustReturn: false)
                }
    

    每次登录按钮点击的时候,利用.withLatestFrom(usernameAndPassword)从usernameAndPassword中获取用户名和密码,然后传给网络请求进行访问,然后显示登录结果,并返回登录结果给上层处理

         signupEnabled = Driver.combineLatest(
                validatedUsername,
                validatedPassword,
                validatedPasswordRepeated,
                signingIn
            )   { username, password, repeatPassword, signingIn in
                    username.isValid &&
                    password.isValid &&
                    repeatPassword.isValid &&
                    !signingIn
                }
                .distinctUntilChanged()
    

    通过validatedUsername, validatedPassword, validatedPasswordRepeated, signingIn四个事件流的状态来决定signupEnabled的状态,也就是决定登录按钮的状态

    相关文章

      网友评论

          本文标题:RxSwift 案例学习(一)

          本文链接:https://www.haomeiwen.com/subject/dzkpittx.html