SwiftUI 和 Combine 编程 (王 巍) (Z-Library)

Author: 王 巍

技术

No Description

📄 File Format: PDF
💾 File Size: 7.8 MB
17
Views
0
Downloads
0.00
Total Donations

📄 Text Preview (First 20 pages)

ℹ️

Registered users can read the full content for free

Register as a Gaohf Library member to read the complete e-book online for free and enjoy a better reading experience.

📄 Page 1
SWIFTER SwiftUI 与 Combine 编程 第⼆版 王巍 (@onevcat)
📄 Page 2
2.0 (2020年 10月) © 2019~ ObjC中国 版权所有 ObjC中国 在中国地区独家翻译和销售授权 获取更多书籍或文章,请访问 https://objccn.io 电子邮件: mail@objccn.io
📄 Page 3
1 简介 8 前置要求 9 适合的读者人群 10 章节结构和推荐阅读方式 10 声明式的 UI构建方式 13 SwiftUI和 Combine简介 17 练习 19 2 你好,SwiftUI 22 计算器 app实例 22 创建项目和 Hello World 23 使用Modifier描述 Text及 Button 27 基本布局和 Stack容器 35 预览多尺寸以及适配 53 总结 55 练习 56 3 数据状态和绑定 62 Calculator模型 63 @State数据状态驱动界面 69 操作回溯和数据共享 79 总结 96 练习 97
📄 Page 4
4 真实世界的 SwiftUI 101 示例 app:PokeMaster 101 开始项目 103 创建列表 105 创建弹出面板 131 创建设置界面 145 总结 151 练习 152 5 Combine和异步编程 157 异步编程简介 157 Combine基础 160 总结 176 练习 176 6 Publisher和常见 Operator 182 准备 182 基础 Publisher和 Operator 185 错误处理 199 Publisher的类型系统 209 总结 213 练习 214 7 响应式编程边界 219 Subject行为 219
📄 Page 5
响应式和指令式的桥梁 229 Foundation中的 Publisher 232 订阅和绑定 238 Cancellable, AnyCancellable和内存管理 246 总结 248 练习 248 8 SwiftUI架构 255 UI调整 255 Swift UI的架构方式 258 通过 Binding改变状态 260 通过 Action改变状态 264 Command和异步操作 275 总结 295 练习 296 9 SwiftUI中的 Combine 301 复合状态驱动 UI 301 实际的网络请求 312 异步图片和图片缓存 327 总结 330 练习 331 10 手势处理和导航 339 手势处理 339 导航层级 351
📄 Page 6
支持 URL Scheme 364 总结 369 练习 370 11 用户体验和布局进阶 373 自定义绘制和动画 373 布局和对齐 387 总结 408 练习 408
📄 Page 7
简介 1
📄 Page 8
2019年春夏之交的这场WWDC,注定会成为深深烙印在 Apple开发者们记忆中的一 场盛会。自从 2014年 Apple发布 Swift以来,还没有哪一次的WWDC能汇集到这 么多人的目光。在这个充满新意和激情的舞台中央,被开发者们给予热情欢呼的主 角毫无疑问只有一个,那就是 SwiftUI。 SwiftUI作为 Apple在自家平台使用 Swift语言打造的首个重量级系统框架,将为这 个平台上用户界面的构建方式带来革命性的转变。它摒弃了从上世纪八十年代开始 就一直被使用的指令式 (imperative)编程的方式,转而投向声明式 (declarative)编 程的阵营,这提高了我们解决问题时所需要着手的层级,从而让我们可以将更多的 注意力集中到更重要的创意方面。 SwiftUI充分利用了 Swift先进简洁的语法,提供了一套完整而优美的领域特定语言 (Domain-Specific Language, DSL)来描述 UI;在漂亮的外表背后,崭新的响应式编 程框架 Combine和早已深深植入于 Swift中的函数式编程思想方式,两者相得益彰, 共同驱动了 SwiftUI的数据流向;在底层,成熟的 iOS系统及强大的 UIKit则保证了 SwiftUI的高效渲染以及与现有特性的无缝衔接;而通用的语法和基础类型,有效降 低了跨平台的难度,模糊了不同目标平台之间的界线,让开发者们更容易把 iOS app 带到macOS,甚至带到web中去。 在本书里,我们会涉及到上面所提到的各个方面。你可以学习到如何使用 SwiftUI来 构建一个现代的 iOS app,包括使用 SwiftUI提供的控件,配置合理的架构和数据流, 按照函数响应式编程 (Functional reactive programming, FRP)的方式来组织逻辑代 码与 UI代码,以及让 SwiftUI app与现有的 UIKit框架有机结合。 书中将提供一些引导教程,指导你动手实践和尝试不同的方案。在本书最后,你应该 能够掌握使用 SwiftUI和 Combine开发 iOS app的基本知识,并依靠教程中所积累 的实际经验,独自进行 SwiftUI app的开发。
📄 Page 9
前置要求 阅读本书,并且跟随实践书中的示例程序,你需要以下技能和环境: → 至少入门级别的 Swift知识:想要基于 SwiftUI进行开发,我们只能使用 Swift语言,但本书篇幅有限,我们并不会在本书中专门介绍 Swift语言的基 础语法和思想。对于某些程序语法上难以理解的部分,我们会进行说明,也会 对 SwiftUI和 Combine框架中新加入的特定用法作出解释。但是构建项目时 的其他部分的代码,需要你自己进行理解。如果你完全没有接触过 Swift,推 荐你可以先阅读 The Swift Programming Language中的教程部分的内容,你 也可以找到这个教程的完整的中文翻译版本。除此之外,我也推荐你了解一些 UIKit开发的基础知识,比如Model-View-Controller (MVC)架构和一些 UIKit 控件的基本使用等。如果你已经是 Apple平台的开发者,那么这个要求应该没 有问题;如果你是从其他平台慕 SwiftUI之名而来,也请放心:UIKit的知识 可以帮助你更好地理解 SwiftUI中的一些概念,但它们并不是必须的。 → 运行macOS 10.15.4或之后版本的mac电脑:Xcode 12支持的最低系统版 本是macOS 10.15.4,如果你的系统版本过低,可能需要先进行升级 (记得备 份重要的资料)。我们推荐安装最新的macOS系统,因为随着 SwiftUI的进 化,会有一些在最新的系统和 SDK中才能使用的 SwiftUI功能。比如 SwiftUI 预览,它可以让你在 Xcode中在编写代码的同时,实时展示结果。这将节省 非常多的时间,并提供最好的 SwiftUI开发体验。 → Xcode 11或之后的版本:Xcode提供了 Apple平台的标准开发环境,从 Xcode 11开始,它所提供的 SDK中才包含有 SwiftUI和 Combine框架。你 可以在Mac App Store搜索 Xcode或者到 Apple Developer网站进行下载。 本书当前版本基于 Xcode 12,如果没有特殊理由,你应该始终选择最新的 Xcode版本。
📄 Page 10
适合的读者人群 如上所述,本书面向的主要群体是对新一代 Apple平台 UI构建技术感兴趣的读者。 大致上目标读者的画像可以分为两类: → 现有的 iOS开发从业者:你已经在日常工作中使用 UIKit构建 app (不论是使 用 Swift还是 Objective-C),在 SwiftUI这一新技术出现时,你希望能够了解 和学习这种不同以往的 UI构建方式,并研究今后迁移到使用 SwiftUI的可行 性。SwiftUI和 Combine会为你带来全新的思考方式,就算暂时还没有办法使 用这些新技术,它们也将成为重要的知识储备并帮助你改善现有的程序设计。 → 希望学习 iOS开发的其他平台的开发者:你可能会拥有一些响应式框架或者声 明式编程的经验 (比如使用 React,Flutter或者各类 Rx框架)并开发过一些客 户端的项目;你可能单纯只是对 Swift语言或者 iOS开发感兴趣,而实际上做 的是完全不相关的后端、运维、或者甚至产品经理的工作;你也可能只是刚刚 进入到开发者的队伍还没有任何项目经验。相比于 iOS开发中传统的 UIKit, SwiftUI要简单得多。也就是说,SwiftUI可能会是一个完美的入门 iOS开发 的契机。不过,我还是建议你先对 Swift有部分了解后再继续。 如果你对 Swift有兴趣,我们也准备了一些其他书籍,其中涵盖了关于函数式开发, 语言进阶,优化以及 app架构等很多方面的话题。你可以在「ObjC中国」的在线商 店找到所有这些图书: https://objccn.io/products/ 章节结构和推荐阅读方式 按照话题,本书被由浅入深地分为三个部分。
📄 Page 11
第一部分,SwiftUI初步 在这部分中,我们首先会通过创建一个计算器 app来介绍 SwiftUI的基本概念,包 括:通过使用 View来描述 UI;基本控件和容器的使用;利用modifier来配置 UI; 调整布局等内容。这些步骤让我们可以得到一个不错的用户界面。接下来,我们会实 际使用事件和模型数据让计算器正常工作。 在完成这个相对简单的例子后,我们会构建一个更贴近于实际,也更复杂的例子。其 中会涉及到 iOS app中的 Navigation (导航),List (列表),动画以及其他一些 iOS上 常见的交互。通过这个复杂例子,你可以学习到如何使用 SwiftUI构建出一个常见的 iOS app。 第二部分,Combine框架 Combine框架非常适合用来配合声明式的 SwiftUI,创建出稳定和单向的数据流动。 相比于传统的MVC架构,使用在Model和 View间使用数据绑定,并将底层事物交 由系统处理,可以大大简化开发工作流程。我们会从 FRP的基础开始,介绍 Combine框架的基础概念,包括 Publisher,Subscriber,各类 Operator等。 Combine在背后驱动了 SwiftUI的数据流动。虽然就算不了解 Combine,我们也能 写出合理的 SwiftUI app,但是对它进行了解,有助于我们更好地理解架构 app的方 式。这部分内容偏向于理论,它将为后面的实践内容提供基础和背景知识。 第三部分,综合实践 在最后一部分中,我们会使用之前准备的 app的基本框架,以及我们学习到的 Combine的相关知识,来完成示例 app的实现。我们会使用响应式编程的方式实际 地从网络 API获取数据,并将它们绑定到 SwiftUI上。你可以学习到如何将 Combine框架中的概念运用到日常开发里,以此改善代码设计。
📄 Page 12
SwiftUI还处于早期阶段,有很多不足之处。为了打造一款优秀的 app,我们有时候 也需要和 UIKit混用。SwiftUI提供了展示 UIKit内容的接口,可以让我们在遇到无 法解决的问题时回滚到 UIKit。另外,像是错误处理、用户状态存储等话题,也会涵 盖在本章中。 如何阅读本书 本书的章节之间有较强的相关性,即使你有 UIKit开发的基础,或者有 RxSwift等 FRP的知识,也还是建议从头开始通读本书。书中内容假定你对 SwiftUI几乎没有过 接触和经验,并会以详细的示例引领你一步步在构建 app中掌握必要知识。实际动 手会对理解本书内容有很大帮助,因此最好是能够一边打开 Xcode敲写代码一边阅 读本书。 在每章结束后,我们为读者准备了若干的练习题以及一些挑战项目。挑战项目的内 容一般是对于示例 app的代码不足之处的一些补完任务,为了不限制思路,我们并 没有提供标准答案:实践才是检验真理的途径。如果能够完成这些可选内容,相信会 对加深理解大有裨益。 需要特别说明的是,书中为示例 app提供的实现方式只是一个可以完成任务的参考。 在本书写作时,SwiftUI还是非常年轻的框架,大家对 SwiftUI的热情探索才刚刚开 始。在许多问题上,SwiftUI框架本身还有欠缺,相关的最佳实践也都还没有形成, 因此,书中提供的方式不一定是最优秀的,甚至有时候还会显得笨拙。我十分推荐在 每个小任务完成后,读者能够自己再进行思考,尝试去寻找出更优秀的解决方案。古 有云,“弟子不必不如师,师不必贤于弟子”,阅读书籍亦是如此。笔者本身水平有 限,书中也难免会有谬误,如有发现,还请不吝赐教,让我们加深理解,一起进步。 欢迎你到本书的公共 GitHub仓库的 issue页面自由发表你的看法,或者讨论各类问 题。 接下来,我想先对本书的几个重要话题先做一些概要性的说明介绍。
📄 Page 13
声明式的 UI构建方式 指令式编程 SwiftUI是一个声明式的 UI开发方式。在能够进一步之前,我们最好先弄清指令式 和声明式两种编程方式的区别。 C,C++和部分更早期的语言遵从指令式编程的范式。一般来说,指令式编程支持三 种语句: 1. 运算语句:将某个值保存到变量中,计算某个表达式的结果,或者方法调用。 比如 let a = 1 + 2就是一个标准的运算语句。 2. 循环语句:在特定条件下反复运行某些语句,比如 for和while。 3. 条件语句:如果某些条件成立时才运行某个区块的代码,否则就将省去。比如 if和 switch都是条件语句。 通过组合这些语句,我们在指令式编程中逐条指示计算机如何工作。早期的指令式 编程语言都是针对计算机本身的机器或汇编语言。在这些语言中,指令的设计贴近 计算机的运算硬件设计,这让硬件的运行更容易和高效。在随后的早期高级语言中, 对这些指令语句的映射往往成为了设计语言的主流方向。虽然这有利于编译器的编 写和最终的运行效率,但同时也阻碍了复杂程序的设计。 汇编距离我们有些遥远了,让我们回到 Swift,来看一个典型的指令式编程的例子。 比如我们有一个学生系统,记录了学生姓名,并用一个字典保存各科目的考试成绩: struct Student { let name: String let scores: [科⽬: Int] }
📄 Page 14
enum 科⽬: String, CaseIterable { case 语⽂, 数学, 英语, 物理 } 假设我们有一些学生的数据: let s1 = Student( name: "Jane", scores: [.语⽂: 86, .数学: 92, .英语: 73, .物理: 88] ) let s2 = Student( name: "Tom", scores: [.语⽂: 99, .数学: 52, .英语: 97, .物理: 36] ) let s3 = Student( name: "Emma", scores: [.语⽂: 91, .数学: 92, .英语: 100, .物理: 99] ) let students = [s1, s2, s3] 我们现在想要检查 students里的学生的平均分,并输出第一名的姓名。使用指令式 的方式,依靠运算,循环和条件语句,可以给出下面这种解决方案: var best: (Student, Double)? for s in students { var totalScore = 0 for key in 科⽬.allCases { totalScore += s.scores[key] !" 0
📄 Page 15
} let averageScore = Double(totalScore) / Double(科⽬.allCases.count) if let temp = best { if averageScore > temp.1 { best = (s, averageScore) } } else { best = (s, averageScore) } } if let best = best { print("最⾼平均分: \(best.1), 姓名: \(best.0.name)") } else { print("students 为空") } 如果第一次读这段代码的话,想要了解它到底做了什么或者到底最后会得到怎样的 结果,可能必须要仔细阅读并理解每一行指令。代码行数与 bug多少往往是正相关, 错误很可能隐藏在某个循环或者判断里,这种开发方式为代码的正确性和后续维护 带来了巨大的挑战。 我们有什么办法可以减轻开发者的负担,让计算机更加 “智能”地为我们解决问题呢? 声明式编程 声明式的编程范式正好站在指令式的对面:如果说指令式是教会计算机 “怎么做”, 那么声明式就是告诉计算机要 “做什么”。指令式编程是描述过程,期望程序执行以 得到我们想要的结果;而声明式编程则是描述结果,让计算机为我们考虑和组织出 具体过程,最后得到被描述的结果。
📄 Page 16
现代语言中,一般使用函数式编程或者 DSL的方式来实现声明式的编程方式。 我们不会在本书中再详述关于什么是函数式编程的概念,如果你对函数式编程的相 关话题感兴趣,笔者推荐另一本《函数式 Swift》的图书,你可以在 ObjC中国的网 站上找到相关资料。对于上面的例子,使用函数式编程的方式,可以将它改写为: func average(_ scores: [科⽬: Int]) !" Double { return Double(scores.values.reduce(0, +)) / Double(科⽬.allCases.count) } let bestStudent = students .map { ($0, average($0.scores)) } .sorted { $0.1 > $1.1 } .first 在这段代码中,我们首先将 students映射为了 (Student,平均分)的数组,然后对平 均分按降序进行排序,最后取出排序后的首个元素。在这个过程中,我们仅仅是用语 句描述了我们想要的结果,例如:按规则进行映射、对元素进行排序等。我们并不关 心代码在底层具体是如何操作数组的,而只关心这段代码能够得到我们所描述的结 果。 另一种经常用来实现声明式编程的方法是领域特定语言 (DSL),其中一个典型的代表 是 SQL。SQL被用在关系数据库中,专门用在结构化查询这一特定领域,它通过描 述期望的结果来对数据库进行查询。上面的例子在 SQL中的对应语句如下: select name, avg(score) as avg_score from scores group by name order by avg_score; 不论是使用函数式的方式,还是使用 DSL的方式,我们都能够比较轻松地阅读代码, 更快速地理解代码的意图。指令式编程更偏向于是 “写给计算机的语言”,而相对地,
📄 Page 17
声明式编程则更偏向于 “写给人看的语言”。将具体的步骤和工作交给底层,同时也 最大限度避免了由于开发者的错误而造成的 bug。 声明式的 UI 使用声明式的编程方式来进行用户界面开发,在近年来是颇为热门和受到欢迎的实 践方式。当前流行的声明式 UI的思想,可以追溯到 Elm语言的设计。在之后, React的 Component和 Flutter的Widget都或多或少继承了这种思想,这类声明 式 UI都有如下特点: 1. 代表 UI层的 View,一般来说并不是真实负责渲染的传统意义的视图层级,而 是一个 “虚拟的”对 View组织关系的描述 (声明)。 2. 决定 UI的用户状态 State被存储在某个或某几个对象中。 3. 用一个函数描述 View,这个函数的输入参数是 State,即 View = f(State)。 4. 框架在 State改变时,调用上述函数获取对应新的 State的 View,并与当前 的 View进行差分计算,并重新渲染更改的部分。 一般来说,View = f(State)中的函数 f是纯函数,也就是对于某个特定的输入 State, 所对应的 View是确定的,不随其他变量而改变。我们可以单纯地通过控制和改变 State来得到确定的 UI,这是使用声明式的方法来构建 UI的基础。 如果你对这种方式的 UI编写还心存疑虑或颇有不解,那很正常。本书的主要任务就 是通过一些实例打消你的这种疑虑,让你能够熟悉和掌握声明式的 UI编程方式。在 接下来的几章中,我们将会多次看到上述的四个特点,并依次逐渐展开详述每个部 分的内容,让你获得一些基本的声明式 UI编程经验。 SwiftUI和 Combine简介 现在,可以来谈一谈本书的两个主角了。
📄 Page 18
SwiftUI和 Combine都是在WWDC 2019上 Apple公布的开发框架,它们都是由纯 Swift编写的。前者是一个声明式 UI的用户界面开发框架,后者是基于响应式编程, 用于处理数据流的框架。SwiftUI依赖 Combine来进行背后的数据处理部分的工作。 得益于 Swift 5所带来的 ABI稳定,现在 Apple可以放手在 iOS和macOS等系统中 内置 Swift运行环境,这也使得 Apple自己使用 Swift编写系统级别的开发框架成为 可能。SwiftUI和 Combine正是在这个背景下所诞生的首批 Swift系统级框架。 在 Swift诞生之前,Apple平台开发所使用的 Objective-C语言已经有快三十年的历 史了,大家也已经早已习惯了 AppKit和 UIKit所提供的一套编程范式。不论是在 macOS上还是在 iOS上,使用Model-View-Controller (MVC)的架构,配合 Target-Action或者 protocol-delegate模式来交换信息,使用 Key-Value Observing (KVO)或者 Key-Value Coding (KVC)来灵活地监测变化和读写属性,已经 是大部分 Apple平台开发者驾轻就熟的事情了。这一套 “成熟”的工具在过去很好地 服务了开发者,并帮助我们创造了无数美好的 app。但是,随着移动端的不断发展, 现代开发的节奏越来越快,项目的复杂度也越来越高。像是 React Native和 Flutter 这样的移动端跨平台方案大行其道,它们在开发时可以进行热重载,不需要重新编 译和运行就可以看到 UI部分修改的结果,这大大加速了开发进度。而两者声明式 UI 的编写方式和严格的数据流动方向,也大幅减轻了开发者的思考负担。由于这些先 进特性所带来的良好体验,以及一次编写,多平台运行的独特魅力,导致了越来越多 的 native开发者 “流失”到这些平台,这对 Apple自身的生态也是一种威胁。 因此,SwiftUI和 Combine作为首批系统级别的 Swift框架,承担了双重任务。 首先,它们需要作为 Swift语言的推广,让大家看到这门语言与 Apple生态的结合 究竟能够产生怎样的优势。Swift语言虽然是开源项目,但毫无疑问 Apple依然主导 着这门语言的发展方向。Swift 5.1中许多特性,都是为了能更简洁地使用 SwiftUI 和 Combine而新增的。相比于其他竞品,由于编程语言上的完全控制,SwiftUI和 Combine在使用上来说都比其他方案更加简明,可读性也更高。这对于吸引新人加 入到 Swift开发生态圈至关重要。
📄 Page 19
其次,这两个框架还担负着 Apple平台编程范式转变的重要任务。越来越多的案例 证明,声明式和响应式可以有效加速进度,并减少开发者出错的可能性。而 Objective-C积重难返,想要承担起这样的转变,可能需要花费更多的时间和精力。 在如今 React Native、Flutter以及微信小程序的 “围杀”下,如何为开发者提供更好 的创作环境,持续地吸引开发者留在自己的平台,是 Apple不得不认真考虑的问题。 相对于高度成熟的 UIKit和 AppKit来说,SwiftUI还十分年轻,它周围的生态也远远 没有达到成熟。不过,SwiftUI在 Apple平台上是与 UIKit和 AppKit兼容的:你可以 在已有的 app中加入某些使用 SwiftUI制作的界面,也可以在 SwiftUI中使用任何 已有的 UIKit和 AppKit控件。这让开发者们不需要冒险一次性地迁移到新的平台, 而可以对新事物 “渐进式”地一点点尝试,也可以在任何时候 “返回”到熟悉和完备的 解决方案。拥有这个保证后,开发者就完全可以放心地将 SwiftUI引入到项目中,而 不用担心最终会有什么需求无法实现。 练习 1.区分指令式和声明式 下列语句分别是哪种思想的体现?是指令式还是声明式? a. 去超市买一只鸡,然后用微波炉 600W加热三分钟以后装盘。 b. 将文件夹中尺寸大于 1MB的文件按修改日期排序并打印它们的文件名。 c. 画一组圆心在 (0, 0),半径以 5逐渐增加的圆。 d. 将画笔移动到 (0, 0),从 (0, 0)到 (1, 1)画一条线。 2.关于 SwiftUI 下面关于 SwiftUI的说法,不正确的一项是什么?
📄 Page 20
a. SwiftUI是 Apple推出的声明式 UI开发框架,它由 Swift开发。 b. SwiftUI可以和现有的 UIKit和 AppKit兼容,因此现有 app可以逐步迁移到 SwiftUI。 c. SwiftUI使用了大量 Swift 5.1的特性来简化语法和降低学习难度。 d. 在 SwiftUI中,我们使用MVC和 Target-Action等开发范式来组织数据流动。 3.实践指令式和声明式处理问题 对本章代码中的 students数组,分别使用指令式和声明式 (通过函数式编程)的方式 完成下列任务: 1. 计算所有学生的语文成绩平均分,并将其打印出来。 2. 统计各个科目的及格率 (60分以上及格),并将及格率结果进行排序。
The above is a preview of the first 20 pages. Register to read the complete e-book.

💝 Support Author

0.00
Total Amount (¥)
0
Donation Count

Login to support the author

Login Now
Back to List