RxSwift 案例学习(一)

本文是官方案例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的状态,也就是决定登录按钮的状态

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 前言 在之前用Objective-C语言做项目的时候,我习惯性的会利用MVVM模式去架构项目,在框架Reactiv...
    Tangentw阅读 21,242评论 32 124
  • 概述 RxSwift顾名思义是Swift的一种框架,您或许曾经听说过「响应式编程」(Reactive Progra...
    Mr大喵喵阅读 1,920评论 3 4
  • 简介 最近接手了一个N年前的老项目,由于实在是过于陈旧,于是提出想要重构项目该项目的想法,没想到和项目经理竟然达成...
    Yeeshe阅读 903评论 0 1
  • 这篇文章演示了如何运用Reactive Programming的思想开发一个用户登录页面,包括用户名和密码的验证,...
    最Fly的Engine人阅读 10,845评论 18 45
  • 前言RxSwift的目的是让数据/事件流和异步任务能够更方便的序列化处理,能够使用Swift进行响应式编程。 本文...
    努力奔跑的小男孩阅读 1,912评论 0 3