美文网首页
响应式编程综合演练

响应式编程综合演练

作者: CatLogic | 来源:发表于2017-03-13 18:33 被阅读0次

    介绍

    此篇文章仅献给想学习响应式编程的初学者。
    demo是一个完整的登录功能的实现,包含第三方:

    • RxSwift
    • RxCocoa
    • Moya
    • SwiftJson
    • ObjectMapper
    • Alamofire
      实现了登录页面的输入校验,http设置及请求,模型转换,缓存,自定义错误处理插件

    关于请求缓存的举例在登录中不太恰当,只是用来说明如何实现

    demo结构介绍

    • DMUser: 用户模型
    • DMLoginViewController: 登录控制器
    • DMLoginResult:登录状态(枚举)
    • DMLoginViewModel:数据业务处理
    • DMLoginProtocols: 登录所用到的接口(协议)
    • DMLoginService:服务(具体类,实现了所定义的协议接口)
    • DMLoginHttp:遵从Moya的TargetType,请求定义
    • DMDeployProvider:提供请求的额外配置,包含:request和session manager
    • DMMoyaHttpErrorHandlePlugin: 自定义插件,可用来统一处理错误
    • DMCache: 缓存,并扩展RxMoya支持缓存

    数据校验

    关于登录的输入校验,直接使用RXSwift官方demo内的GitHubSignup的UsingDriver > 2,看过的可以跳过该章节。

    首先介绍下driver,
    关于driver和observable的区别,官方是这样说的:
    * Can't error out (不能错误终止信号)
    * Observe on main scheduler (在主线程观察)
    * Subscribe on main scheduler (在主线程订阅)
    * Sharing side effects (分享副作用,shareReplay())
    
    官方示例中 UsingDriver > 2 是UsingVanillaObservables > 1的优化版本
    

    介绍下官方示例

    官方示例采用的是MVVM + OOP的方案实现。
    在控制器中,将输入源(文本框,按钮等事件)和实现协议的服务类实例传递给viewmodel中,然后在viewmodel中去观察输入源的变化并处理,在控制器中去订阅这些信号(处理过的信号)。流程大体是这样的,具体参考代码,这里重点介绍两处:

    关于数据的传递,也可以称为数据的流向。

    由于swift闭包的特性,闭包内参数未显示声明参数的类型,或者直接省略了参数列表,而已$0, $1代替,造成了理解上的不方便。

    //viewmodel中校验用户名
    validateUserName = input.userName.flatMapLatest {
                return validService.validationUserName($0)
                  .asDriver(onErrorJustReturn: .failed(message: "链接服务失败"))
     }
    //校验方法声明
    func validationUserName(_ usernName: String) -> Observable<ValidationResult>
    //校验请求方法声明
    func checkUserNameAvaliable(userName: String) -> Observable<Bool>
    //控制器中的UI绑定
    viewModel.validateUserName
              .drive(userNameValidLabel.rx.validationResult)
              .addDisposableTo(disposeBag)
    

    在这一个流程中,数据是如何传递与使用的呢?

    初学者刚开始接触可能难免困惑,这一点要提及下函数式编程几个概念(Functor,Applicative,Monad),有兴趣的可以阅读下,有助于理解。
    个人理解:将一个值a封装为上下文值A,运用一个处理普通值但返回上下文中的值的函数B,得到另一个上下文中的值C,然后不断传递再处理,就形成了响应式编程实现的这种独特写法,还可以参考:
    雷纯锋的技术博客1
    雷纯锋的技术博客2

    解析上方代码片段:

    1. 在viewmodel中去观察文本框的变化,flatMapLatest是获取最新的舍弃旧的,该闭包内省略的参数是文本框的文本,即textField.text <=> $0,将文本传递给校验方法;
    2. 在校验方法中,完成对该文本的校验,并返回一个可观察的对象(不知道怎么称呼,就以单词意思解释了)Observable<ValidationResult>,是对ValidationResult进行了封装(可以理解讲一个普通值变为了一个上下文中的值,方便链式调用),在该方法中又调用了校验请求的方法;
    3. 在校验请求的方法中,实际发起了网路请求,并对网络请求结果进行处理,返回Observable<Bool>这样的值(可以理解为将一个普通的bool类型的值封装为了一个上下文中的值),在校验方法中又将该
      Observable<Bool>转化为了Observable<ValidationResult>;
    4. 回到viewmodel中,validService.validationUserName()这个方法就是拿到上一步的处理结果Observable<ValidationResult>,而driver是特殊的observable,只要满足条件就可以转化为driver,但由于observable可以出现错误终止信号,而driver不可以,所以driver捕获该错误将之转化为发送其它正常信号;
    5. 控制器中viewModel.validateUserName.drive拿到了之前的处理结果Observable<ValidationResult>,接下来就是如何将结果具体绑定到UI上。
    处理结果与UI的绑定
    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
            }
        }
    }
    

    对于RxCocoa支持的可以直接使用,但不支持的或者需要自定义的就要实现这个方法,此处对于Reactive进行扩展,实现了label对于ValidationResult的自定义展示。

    网络请求

    网络请求采用的是比Alamofire更高一层的抽象,Moya,并且也支持了RxSwift。
    基本用法不多介绍,参照demo(DMLoginHttp,DMLoginService)或其它博客,这里介绍下对它的其它的一些自定义。
    首先介绍下它的初始化参数,都有默认值,可以省略,但业务总是复杂多变的,需要我们的定制,所以先了解下它的各项参数含义以便配置。可见官方文档 Providers.md

    public init(
    endpointClosure: @escaping (Target) -> Moya.Endpoint<Target> = default, 
    requestClosure: @escaping (Moya.Endpoint<Target>, @escaping Moya.MoyaProvider.RequestResultClosure) -> Swift.Void = default,
    stubClosure: @escaping (Target) -> Moya.StubBehavior = default, 
    manager: Moya.Manager = default,
    plugins: [PluginType] = default,
    trackInflights: Bool = default)
    
    1. Moya的方式是:endPoint -> request,所以在此处可以对请求进行再设置,包含url,header等;
    2. 接收之前的 endPoint生成request的实例,在此可对request再设置;
    3. 用来控制模拟请求的,即返回的是本地数据;
    4. 用来生成Alamofire的sessionManager实例,此处可进行超时时间,https自签名证书的设置等,假如你之前直接使用Alamofire的manager的设置都可以移植到此处设置,;
    5. 插件设置,本身提供了三种插件(NetworkActivityPlugin,CredentialsPlugin,NetworkLoggerPlugin),还可以自定义实现插件,只需实现插件协议<PluginType>
    6. trackInflights ??(额暂时不知道,有知道的可以告诉我下)

    此demo我自定义了一下相关的:

    • DMDeployProvider:定义了endPoint和manager
    • DMMoyaHttpErrorHandlePlugin: 自定义插件,错误统一处理

    缓存

    由于Moya未提供离线缓存,因此需要自己扩展Moya实现缓存。此例子中缓存是以字典形式存在内存中的,可替换为真正的缓存框架,此处仅仅是为了展示如何扩展缓存。

    DMCache.swift中,
    定义了缓存策略:DMCacheType
    缓存类(假的,示范用): DMCache
    扩展:是Moya支持缓存,tryCache()

    核心代码:
    return Observable.create({[weak self, weak cache]  observe -> Disposable in
                
                switch cacheType {
                case .onlyCache:
                    print("使用缓存!!!")
                    if let response = cache?.getValue(forKey: identifier) {
                        observe.onNext(response)
                    }
                    observe.onCompleted()
                case .cacheThenRequest:
                    print("先使用缓存再请求数据!!!")
                    if let response = cache?.getValue(forKey: identifier) {
                        observe.onNext(response)
                    }
                    fallthrough
                case .onlyRequest:
                    print("请求数据!!!")
                    task = self?.request(target) { result in
                        switch result {
                        case let .success(response):
                            observe.onNext(response)
                            observe.onCompleted()
                            cache?.storeValue(response, forKey: identifier)
                        case let .failure(error):
                            observe.onError(error)
                        }
                    }
                }
                return Disposables.create {
                    task?.cancel()
                }
            })
    
    

    此处仅需要依据不同的缓存策略,包装实现即可。
    注意fallthrough关键字,小心坑~~ :)

    模型

    最后拿到数据,使用ObjectMapper进行JSON转模型即可,不多介绍


    总结:此demo示例了如何用响应式编程将众多功能结合在一起,着重介绍了RxSwift和Moya的结合使用,后续会重点关注RxSwift的实现原理及全方位讲解使用。

    看~灰机灰过来了~灰机又灰过去了~
    

    相关文章

      网友评论

          本文标题:响应式编程综合演练

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