什么是SwiftUI?
苹果开发平台的新UI框架,基于swift。由于swift ABI 稳定,将会诞生更多的swift框架。SwiftUI是用来替换原IOS平台的UIKit和macOS的APPKit的UI框架,特性是声明式的编程。
什么是Combine?
Combine框架为你的应用程序处理事件提供了一种声明性的方法。你可以为给定的事件源创建单个处理链,而不是潜在地实现多个委托回调或完成处理程序闭包。链的每个部分都是一个合并运算符,对从上一步接收到的元素执行不同的操作。(类似于RXSwift中的Observable和各种操作符)
响应式编程:
对于苹果的平台,已经有几个第三方响应式框架,比如RxSwift,它实现了Rx标准;ReactiveSwift,它受到了Rx的启发;intercellar,它是一个自定义实现。
Combine实现了一个与Rx不同但类似的标准,称为响应流。响应流与Rx有一些关键的区别,但它们都有着大多数相同的核心概念。
如果您以前没有使用过上面提到的一个或另一个框架,请不要担心。到目前为止,反应式编程已经成为苹果平台的一个相当小的概念,特别是在Swift中
如果你之前有RXSwift或者ReactiveCocoa的响应式编程经验,你将很容易学习SwiftUI+Combine编程。如果没有这部分知识,没有关系,并且你可以忽略以下文章中所有关于RXSwift的内容,但你需要了解这将是一种和原本UIKIt和AppKit都不同的UI编程方式,原本的UIKit更新界面操作的是UI组件对象的属性,而新的编程方式则是将数据和UI进行绑定,需要更改UI界面时,直接修改数据源或者页面状态,而UI页面将自动过渡到新的状态。
异步编程:
原本的Foundation+UIKIt/AppKit通过通知、代理、闭包和GCD等
提供了丰富的异步编程方式,这些处理方式在过去一直服务于苹果应用开发,在SwiftUI早期我们仍然可能会用到这部分内容,但是SwiftUI+Combine也提供了其独有的异步处理事件方式
Combine旨在向Swift生态系统引入一种新的语言,帮助您在异步编程世界的混乱中带来更多的秩序。苹果已经将Combine的API深深地集成到了基础框架中,因此Timer、NotificationCenter和核心框架(比如CoreData)已经在使用它的语言。幸运的是,Combine也很容易集成到您自己的代码中。
为了让您了解Apple如何致力于使用Combine进行响应式编程,这里有一个简单的图表,显示了Combine在系统层次结构中的位置:
各种系统框架,从Foundation一直到SwiftUI,都依赖于Combine,并提供Combine集成作为其“传统”api的替代。
因为Combine是一个Apple框架,所以它并不是要取消经过良好测试的、可靠的api(如Timer或NotificationCenter)的角色。这些基础类型仍然存在,并尽了自己的职责。相反,Combine与它们集成,并允许应用程序中的所有类型通过一种新的通用语言彼此异步对话。
因此如果理想的话我们可以使用同一种异步工具链接应用的各个部分 ,从数据模型到网络层直至用户界面。
何时可以使用新的框架
在iOS 13/macOS Catalina中,苹果通过内置的系统框架Combine为其生态系统带来了响应式编程支持。
与苹果的任何新技术一样(比如当初的Swift),它的应用程序起初都有点有限:您只能将Combine用于支持iOS 13/macOS Catalina或更高版本的应用程序。但与苹果押注的任何技术一样,它的支持将迅速普及,对Combine编程的技能需求将激增。
话虽如此,从学习Combine的一些基本知识开始,看看它如何帮助您编写安全可靠的异步代码。
Combine基础
简单来说Combine编程具有三个重要成员:publishers、operators、subscribers
,我们将在之后慢慢介绍着三大成员以及适用于这些成员的操作符。
Publishers
publishers可以随时间推移向一个或多个订阅者(如subscribers)发送值类型数据。不管Publisher的内部逻辑(几乎可以是任何东西,包括数学计算、联网或处理用户事件),每个Publisher都可以发出这三种类型的多个事件:
泛型类型的值(类似RXSwift的next事件)。
successful completion完成事件。(类似RXSwift的complete事件)
error completion 错误事件。(类似RXSwift的error事件)
一个publisher 可以发出零个或多个值,如果发出success或者error事件这不再发出更多值
下面是发布Int值的Publisher在时间线上的可视化效果:
publishers也内置了错误处理,因此如果你需要的话,你也可以自定义错误处理。
Publisher协议有两个泛型类型,从上图中可以看出:
•Publisher.Output是Publisher的输出值类型。如果publisher专门化为Int,则它永远不会发出字符串或日期值。
•Publisher.Failure是publisher在失败时可能引发的错误类型。如果发布服务器永远不会失败,则可以使用Never来指定。
当您订阅一个给定的Publisher时,你知道期望从它获得什么类型的值,以及它可能会失败的错误。
Operators
操作符是在Publisher协议上声明的方法,返回相同或新的Publisher。(类似于Swift标准库中数组的操作符map、filter、zip
等的作用,RXSwift中也是类似的)
操作符是高度耦合和可组合的,当一个操作符的输出试下一个操作符的输入时你可以想拼图一样将他们组装在一起,的到想要的值
用明确的声明式方法定义每个处理过程,你可以确定每个处理的顺序,以及得到正确的输入输出值类型
Subscribers
最后,你到达订阅链的末端:每个订阅链都以Subscriber结束,来对Publisher发出的输出或完成事件执行“操作”。
目前,Combine提供了两个内置的subscriber,这使得处理数据流变得简单
- sink subscriber 允许您提供闭包和接收值和完成事件。你可以订阅到任何发出的事件。
- assign subscriber允许您在不需要自定义代码的情况下,将结果输出绑定到数据模型或UI控件上的某个属性,以便通过key Path直接在屏幕上显示数据。
如果您对数据有其他需求,创建自定义Subscriber甚至比创建Publisher更容易。Combine使用一组非常简单的协议,允许您构建自己的自定义工具。
Subscriptions
当您在订阅链的末尾添加subscriber时,它会在链的开头一直“激活”publisher。这是一个需要记住的奇怪但重要的细节-如果没有subscriber潜在地接收输出,则publisher不会发出任何值。
一旦订阅代码编译成功,并且自定义代码中没有逻辑问题,就完成了!按照设计,每当某个事件(如用户手势、计时器关闭或其他事件)唤醒某个publisher时,订阅将异步“启动”。
更好的是,由于Combine提供了一个称为Cancellable的协议,您不需要专门管理内存订阅。
两个系统提供的subscriber都符合Cancellable协议的,这意味着您的订阅代码(例如,整个publisher、operator和subscriber调用链)返回一个可取消的对象。每当您从内存中释放该对象时,它将取消整个订阅并从内存中释放其资源。(作用类似RXSwift DisposeBag)
例如,这意味着您可以通过将订阅存储在视图控制器上的属性中,轻松地管理“绑定”订阅的生命周期。这样,每当用户从视图堆栈中解除视图控制器时,将释放属性,同时也将取消您的订阅。
应用程序架构
可能碰到这个问题时那你会说这超出了你学习的预期值,但是不要担心,Combine不是一个影响应用程序结构的框架,你可以在MVC(Model-View-Controller)应用程序中使用Combine,也可以在MVVM(Model-View-ViewModel)代码、VIPER等中使用Combine。您可以迭代和有选择地添加Combine代码,只在希望在代码库中改进的部分使用它。这不是一个“要么全有要么全无”的选择。
如果你同时采用Combine和SwiftUI,情况就稍微不同了。在这种情况下,将C从MVC架构中删除确实是有意义的。但这要归功于Combine和SwiftUI的协同使用——这两种物资在同一个房间里都会发生反应。
你不再需要Viewcontroller去控制你的视图显示,当你的到数据后这一切都交给SwiftUI和Combine处理就行了