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,那么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 {
        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,
            password: Driver,
            repeatedPassword: Driver,
            loginTaps: Driver
        ),
        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的结果为后面处理做准备

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 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 案例学习(一))