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的状态,也就是决定登录按钮的状态

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容

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