使用Swift进行响应式编程的实战指南

发表时间: 2023-03-27 14:31

目录

1.什么是响应式编程

2.函数式Swift

3.MVVM

4.MVVM与响应式结合

5.总结

一.什么是响应式编程

Wiki上的解释: Reactive programming 是一种面向数据串流和变化传播的声明式编程范式。

iOS客户端的原生开发使用 Objective-C 和 Swift 开发,使用 Objective-C 的时候注重面向对象编程,大多数都是使用命令式的编程,Swift更注重面向协议编程、函数式编程

做过iOS客户端的同学一定了解过 KVO 这个内置的Api,KVO可以帮助我们将属性的变更和变更后的处理分开,简单的理解就是一个对象的属性改变后,另外一连串对象的属性都随之发生改变。KVO的写法和使用上比较复杂,而且只支持 NSObject ,局限性太大。

Apple在推出 Swift 之后,响应式的编程基于数据流的理念,异步的处理事件和函数式编程能很好的结合,ReactiveX 推出了响应式的库RxSwift,WWDC 2019 上 Apple 公布了声明式全新界面框架 SwiftUI,以及配套的响应式编程框架 Combine,Combine只支持iOS13以上的系统,毕竟属于原生的框架,在性能上要稍微强于 RxSwift,根据支持的版本差异开发者可以自行选择。

最新推出Rx的微软对响应式编程的解释是 Rx = Observables + LINQ + Schedulers,通俗一些的解释就是面向异步数据流编程。数据流可以有多种形式,比如读取一个文件、进行一个网络请求、用户出发的行为等等,都可以认为是一种数据流,当然一个变量也可以认为是一种数据流。

二.函数式Swift

函数式编程中的函数这个词不是指计算机中的函数,而是指数学中的函数,即自变量的映射。也就是说一个函数的值仅决定于函数参数的值,不依赖其他状态。比如 sqrt(x) 函数计算 x 的平方根,只要 x 不变,不论什么时候调用,调用几次,值都是不变的。

1. 一等公民函数与高阶函数

在函数式编程中,函数是一等公民,不再把函数想象成一个处理过程,而是把它当作一个对象或者变量来对待。

在 Swift 中可以很方便的把一个函数赋值给一个常量,这个在 Objective-C 中是做不到的。所谓的高阶函数,指可以将其他函数作为参数或者返回结果的函数,Swift中的函数都是高阶函数,系统库提供了(map,fillter,reduce等)



通过函数这个“管道”,数据从一头经过“管道”到另一头,就得到了想要的数据。

2.柯里化

Swift 里可以将方法进行柯里化 (Currying),这是也就是把接受多个参数的方法进行一些变形,使其更加灵活的方法。函数式的编程思想贯穿于 Swift 中,而函数的柯里化正是这门语言函数式特点的重要表现。



柯里化是一种量产相似方法的好办法,可以通过柯里化一个方法模板来避免写出很多重复代码,也方便了今后维护。

3.闭包

闭包是一个会对它内部引用的所有变量进行隐式绑定的函数。也可以说,闭包是由函数和与其相关的引用环境组合而成的实体。函数实际上是一种特殊的闭包,你可以使用{ }来创建一个匿名闭包。使用 in 来分割参数和返回类型。



上面map函数遍历了数组,用闭包处理了所有元素,并返回了一个处理过的新数组。

那么遵循以上特性,一个好的 Swift 函数式程序会具有一下特质:

● 模块化:相较于把程序认为是一系列赋值和方法调用,函数式开发者更倾向于强调每个程序都能够被反复分解为越来越小的模块单元,而所有这些块可以通过函数装配起来,以定义一个完整的程序。

对可变状态的谨慎处理:面向对象编程专注于类和对象的设计,每个类和对象都有它们自己的封装状态。然而,函数式编程强调基于值编程的重要性,这能使我们免受可变状态或其他一些副作用的困扰。通过避免可变状态,函数式程序比其对应的命令式或者面向对象的程序更容易组合。

类型:一个设计良好的函数式程序在使用类型时应该相当谨慎。精心选择你的数据和函数的类型,将会有助于构建你的代码,这比其他东西都重要。Swift 有一个强大的类型系统,使用得当的话,它能够让你的代码更加安全和健壮。

在实际开发过程中,我们遵循函数式思维去编码,可以很容易地使用 Swift 写出函数式风格的代码,为我们带来以下好处:

1. 方便组件解耦

2. 单元测试和调试都更容易

3. 更方便的代码管理

三.MVVM

MVVM 可以说几乎就是一个 MVC,不过通过 View Model 层来将数据和视图进行绑定。熟悉iOS开发的小伙伴都知道,iOS的 Cocoa 框架都是基于 MVC 设计的,关于 MVC,我们可以看下斯坦福的 CS193p Paul这张经典图



MVC 本身的概念相当简单,同时它也给了开发者很大的自由度。Massive View Controller 往往就是利用了这个自由度,“随意”地将逻辑放在 Controller 层所造成的后果,此时的M不是 Model 已经变成了 Massive。



使用 MVVM 之后可以大大减轻 View Controller 职责,简化后的各个模块分工更加明确,更加方便集成和开发。我们在使用的时候主要遵循一下几个事项:

● ViewController 尽量不涉及业务逻辑,让 ViewModel 去做这些事情,此时的ViewModel实际上的职责是Controller

● ViewModel 绝对不能包含视图 View(UIKit.h),不然就跟 View 产生了耦合,不方便复用和测试

● ViewModel避免过于臃肿,否则重蹈Controller的覆辙,变得难以维护

MVVM 是 MVC 的升级版,完全兼容当前的 MVC 架构,MVVM 虽然促进了UI 代码与业务逻辑的分离,一定程度上减轻了 ViewController的臃肿度,但是 View 和 ViewModel 之间的数据绑定使得 MVVM 变得复杂和难用了,如果我们不能更好的驾驭两者之间的数据绑定,同样会造成 Controller 代码过于复杂,代码逻辑不易维护的问题。

四.MVVM与响应式结合

上面介绍完 MVVM,那应该怎么和响应式结合起来呢?我们先来看下在 iOS 中是怎么进行状态更新的

● Target-Action

● Delegate

● KVO

● Notifications

● Callback

多种回调方式,适用规则、适用场景都不相同,这增加了开发、维护的难度。如果有一种方式可以把状态更新做到统一,那就可以大大提高开发效率,这里就要提到 Rx 了,它把状态变更都转化成流,MVVM 中的 ViewModel 和 Rx 相结合这样就可以做到响应式了。

先来看个例子,一个登录界面

产品经理说了需求:

● 账号是手机号11位

● 密码大于6位

● 当满足上面条件,点击统一条款,下面登录按钮为可点击状态否则不可点击

按照一般思路,2个输入框对应的是2个UITextField控件,还有2个UIButton控件对应单选按钮和登录按钮,我们需要实现这个需求就要做到以下:

1. 在 UITextField 的 Delegate 里面去监听输入的内容

2. 在 UIButton 的 Action 方法里面监听隐私条款是否点击

3. 2个输入框和1个隐私按钮,这3个控件每次更新状态都要去看另外2个是否满足登录按钮可点击

可以见到这样一个简单的需求,在代码实现上要处理的逻辑很多,而且都是分散的,那么我们能不能只罗列条件,然后把这些条件扔给一个条件处理的机制,这个机制就能帮我们正确的处理这些关系?

伪代码如下:

实际处理中会把这些逻辑放到 ViewModel,这样就把 RxSwift,函数式,MVVM 结合起来,达到了响应式编程的样子。

五.总结

Swift 从2014年发布,到iOS13推出的响应式 Combine,开源也一直推动语言的发展,可能未来1天苹果会抛弃原来 Cocoa 框架实现,利用新语言的特性也不是不可能。任何架构和技术都不能满足所有的工程需求,能够使用简单的架构来搭建复杂的工程,制作出让其他开发者可以轻松理解的软件,避免高额的后续维护成本,让软件可持续发展并长期活跃,应该是每个开发者在构建软件是必须考虑的事情。

参考资料:

  1. 关于 MVC 的一个常见的误用
  2. iOS 下的响应式编程