Swift5.5、DocC、Notifications,WWDC21 带来的最大技术变化
WWDC (苹果开发者大会) 2021已经落下帷幕,今年的WWDC 提供了 200 多个深度课程,以帮助开发者了解WWDC2021 引入的新技术,本文会帮国内开发者梳理部分WWDC 2021带来的技术上的变化。
Swift5.5
WWDC2021 给我们带来了Swift 5.5,这是Swift 语言最新的版本,在这个版本中有许多重大的更新,下面会大家详细介绍一下Swift 5.5的一些重要更新。
▐ Swift Concurrency
Swift 5.5 中最大的更新就是引入了全新的并发编程方式,包括async/await语法、结构化并发、Actor等,新的并发编程方式解决了我们以往使用回调的种种缺陷 (嵌套地狱、回调错误处理麻烦、回调分支编写困难等),为开发者带来了极大的便利。
async/await
过去我们编写异步代码都是通过回调的方式,如下:
func processImageData2a(completionBlock: (_ result: Image?, _ error: Error?) -> Void) {
loadWebResource("dataprofile.txt") { dataResource, error in
guard let dataResource = dataResource else {
completionBlock(nil, error)
return
}
loadWebResource("imagedata.dat") { imageResource, error in
guard let imageResource = imageResource else {
completionBlock(nil, error)
return
}
decodeImage(dataResource, imageResource) { imageTmp, error in
guard let imageTmp = imageTmp else {
completionBlock(nil, error)
return
}
dewarpAndCleanupImage(imageTmp) { imageResult, error in
guard let imageResult = imageResult else {
completionBlock(nil, error)
return
}
completionBlock(imageResult)
}
}
}
}
}
processImageData2a { image, error in
guard let image = image else {
display("No image today", error)
return
}
display(image)
}
通过回调的方式编写异步代码有以下缺点:
阅读不直观
嵌套逻辑复杂
错误处理麻烦
分支逻辑难以处理
经常会忘了回调或者返回
在Swift 5.5中为了解决上述回调方式的缺点,引入了async/await语法,可以帮助我们快速的编写异步代码,通过async/await上述代码可以变成如下同步代码:
func loadWebResource(_ path: String) async throws -> Resource
func decodeImage(_ r1: Resource, _ r2: Resource) async throws -> Image
func dewarpAndCleanupImage(_ i : Image) async throws -> Image
func processImageData() async throws -> Image {
let dataResource = try await loadWebResource("dataprofile.txt")
let imageResource = try await loadWebResource("imagedata.dat")
let imageTmp = try await decodeImage(dataResource, imageResource)
let imageResult = try await dewarpAndCleanupImage(imageTmp)
return imageResult
}
正如上述代码所展现的,所有的闭包和缩进都消失了,你可以按顺序依次书写代码——除了 await 关键字,它看起来就和同步代码一样。
关于async函数的工作方式,有一些规则需要注意:
同步函数不能简单地直接调用异步函数, Swift 编译器会抛出错误。
异步函数可以调用其他异步函数,但如果需要,它们也可以调用常规的同步函数。
如果有可以以相同方式调用的异步和同步函数,Swift 将优先选择与当前上下文匹配的任何一个, 如果当前执行上下文是异步的,那么 Swift 将调用异步函数,否则它将调用同步函数。
最后一点很重要,因为它允许库的作者提供他们代码的同步和异步版本,而无需专门命名异步函数。
Structured concurrency
在介绍结构化并发之前,我们先来看一个案例:
func chopVegetables() async throws -> [Vegetable] { ... }
func marinateMeat() async -> Meat { ... }
func preheatOven(temperature: Double) async throws -> Oven { ... }
// ...
func makeDinner() async throws -> Meal {
let veggies = try await chopVegetables() // 处理蔬菜
let meat = await marinateMeat() // 腌制肉
let oven = try await preheatOven(temperature: 350) //预热烤箱
let dish = Dish(ingredients: [veggies, meat]) // 把蔬菜和肉装盘
return try await oven.cook(dish, duration: .hours(3)) // 用烤箱做出晚餐
}
上面处理蔬菜、腌制肉、预热烤箱等都是异步执行的,但是上述三个步骤仍然是串行执行的,这使得做晚餐的时间变长了,为了让晚餐准备时间变短,我们需要让处理蔬菜、腌制肉、预热烤箱几个步骤并发执行
为了解决上述问题,Swift 5.5中引入了Structured concurrency(结构化并发),下面是维基百科中的解释:
结构化并发是一种编程范式,旨在通过使用结构化的并发编程方法来提高计算机程序的清晰度、质量和研发效能。
核心理念是通过具有明确入口和出口点并确保所有生成的子任务在退出前完成的控制流构造来封装并发执行任务(这里包括内核和用户线程和进程)。这种封装允许并发任务中的错误传播到控制结构的父作用域,并由每种特定计算机语言的本机错误处理机制进行管理。尽管存在并发性,但它允许控制流通过源代码的结构保持显而易见。为了有效,这个模型必须在程序的所有级别一致地应用——否则并发任务可能会泄漏、成为孤立的或无法正确传播运行时错误。(来自维基百科)
使用结构化并发,上述制作晚餐的过程可以通过下面的方式进行:
func makeDinner() async throws -> Meal {
// Prepare some variables to receive results from our concurrent child tasks
var veggies: [Vegetable]?
var meat: Meat?
var oven: Oven?
enum CookingStep {
case veggies([Vegetable])
case meat(Meat)
case oven(Oven)
}
// Create a task group to scope the lifetime of our three child tasks
try await withThrowingTaskGroup(of: CookingStep.self) { group in
group.async {
try await .veggies(chopVegetables())
}
group.async {
await .meat(marinateMeat())
}
group.async {
try await .oven(preheatOven(temperature: 350))
}
for try await finishedStep in group {
switch finishedStep {
case .veggies(let v): veggies = v
case .meat(let m): meat = m
case .oven(let o): oven = o
}
}
}
// If execution resumes normally after `withTaskGroup`, then we can assume
// that all child tasks added to the group completed successfully. That means
// we can confidently force-unwrap the variables containing the child task
// results here.
let dish = Dish(ingredients: [veggies!, meat!])
return try await oven!.cook(dish, duration: .hours(3))
}
Actors
class RiskyCollector {
var deck: Set<String>
init(deck: Set<String>) {
self.deck = deck
}
func send(card selected: String, to person: RiskyCollector) -> Bool {
guard deck.contains(selected) else { return false }
deck.remove(selected)
person.transfer(card: selected)
return true
}
func transfer(card: String) {
deck.insert(card)
}
}
actor SafeCollector {
var deck: Set<String>
init(deck: Set<String>) {
self.deck = deck
}
func send(card selected: String, to person: SafeCollector) async -> Bool {
guard deck.contains(selected) else { return false }
deck.remove(selected)
await person.transfer(card: selected)
return true
}
func transfer(card: String) {
deck.insert(card)
}
}
-
Actor 是使用新的 actor 关键字创建的 -
send() 方法被标记为 async,因为它需要异步执行 -
尽管 transfer(card:) 方法没有用 async 标记,但我们仍然需要用 await 调用它,因为它会等到另一个 SafeCollector actor 能够处理请求。
-
两者都是引用类型,因此它们可用于共享状态。 -
它们可以有方法、属性、初始值设定项和下标。 -
它们可以实现协议。任何静态属性和方法在这两种类型中的行为都相同,因为它们没有 self 的概念,因此不会被隔离。
-
Actor 目前不支持继承,这在未来可能会改变 -
所有 Actor 都隐式遵守一个新的 Actor Protocol
更新项 | | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| 不限制 | |
| | |
| | |
DocC
▐ DocC注释编写
/// Eat the provided specialty sloth food.
///
/// Sloths love to eat while they move very slowly through their rainforest
/// habitats. They're especially happy to consume leaves and twigs, which they
/// digest over long periods of time, mostly while they sleep.
///
/// When they eat food, a sloth's `energyLevel` increases by the food's `energy`.
///
/// - Parameters:
/// - food: The food for the sloth to eat.
/// - quantity: The quantity of the food for the sloth to eat.
///
/// - Returns: The sloth's energy level after eating.
///
/// - Throws: `SlothError.tooMuchFood` if the quantity is more than 100.
mutating public func eat(_ food: Food, quantity: Int) throws -> Int {
▐ 从代码注释构建DocC文档
要为 Swift 工程构建文档,请选择Product > Build Documentation。DocC 编译工程的文档并可以在 Xcode 的文档查看器中打开它。
Notifications
在WWDC2021中,系统通知也发生了较大的变化,具体反映在如下几个方面:
▐ 视觉升级
比如用户收到如下通知:
在iOS15系统中开发者可以自定义点击效果,如下图
为了实现上述App icon、内容扩展、动作icon等视觉效果,我们只需要按照下面的方式进行开发:
▐ Focus Mode
Apple 新增了Focus Mode,这个模式可以更好地使通知体验与用户偏好保持一致。
新的专注模式非常适合减少对用户的干扰。iPhone用户可以自定义他们希望收到通知的方式和时间。以前,用户可以通过启用“请勿打扰”模式来选择将所有来电和通知静音。现在,用户将能够通过设置工作、睡眠和个人通知模式来完善他们的通知偏好以适应不同的场景。
对于每个配置文件,用户可以选择要接收通知的应用和联系人、要阻止的应用和联系人,以及要暂停的特定应用功能。用户还可以创建一个主屏幕页面以匹配他们当前的焦点模式并仅显示相关的应用程序。例如,在工作模式下,用户可以选择仅查看与工作相关的应用程序。
焦点配置文件将同步到所有其他苹果设备。 焦点设置也可以由其他设置确定,例如一天中的时间、地理位置或日历事件。
Apple 将使用 AI 自动预测要设置的配置文件。例如,当用户到达工作地点时,iPhone 可以使用地理位置数据来触发工作模式,或者在用户接近就寝时间时使用睡眠时间偏好来触发睡眠模式。
还将有两个与焦点模式相关的新 API。 Status API 告诉应用设备是否处于焦点模式。时间敏感 API 允许应用指定对时间敏感的通知以覆盖设置。
// 返回焦点系统的状态,包括有关当前焦点项目的信息。
class func status() -> UIFocusDebuggerOutput
// 返回系统通知时间敏感的设置
var timeSensitiveSetting: UNNotificationSetting { get }
▐ 通知摘要
用户可以设置对通知进行批处理和优先处理,并选择在一天中的特定时间接收应用程序通知作为摘要。
例如,用户可以将通知分组显示,而不是在整个早上一个接一个地接收通知。
iOS系统将根据用户如何使用不同应用程序而不是应用程序名称和时间来优先处理这些通知。
来自朋友的通知将更接近顶部。带有媒体附件的通知更有可能在摘要中突出显示。
开发人员可以使用新的 relatedScore API 来指示应在此摘要中突出显示应用程序的哪些通知。
/// 系统用于对应用的通知进行排序的权重值
var relevanceScore: Double { get }
▐ iOS 通知权限弹框更新
为了支持上面新的功能,权限提示也在发生变化。
现在,当应用程序请求推送权限时,用户将能够指定他们是要立即从应用程序接收通知,还是将通知组合在一起作为通知摘要的一部分。
▐ 通信通知
新系统添加了将应用程序的通知区分为通信通知的功能。
通信通知将包含发送它们的联系人的头像,并且可以与 SiriKit 集成,以便 Siri 可以根据常用联系人智能地提供快捷方式和通信操作建议。
例如,当用户为焦点模式设置允许的联系人或从您的应用拨打电话时,Siri 将根据您的应用程序提供的意图数据智能地推荐联系人。
要使用通信通知,开发者需要在 Xcode 配置中添加通信通知功能,并实现新 UNNotificationContentProviding 协议的 Intent 对象更新应用程序通知服务扩展中通知的内容。
参考资料
https://onesignal.com/blog/ios-notification-changes-updates-from-apples-wwdc-21/
https://developer.apple.com/documentation/Xcode/documenting-a-swift-framework-or-package
https://developer.apple.com/documentation/xcode/writing-symbol-documentation-in-your-source-files
✿ 拓展阅读
本文分享自微信公众号 - 淘系技术(AlibabaMTT)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Ubuntu 21.10 每日构建版已搭载 GNOME 40
GNOME 40 终于来到了Ubuntu 21.10 每日构建版。 虽然 GNOME 40 已正式发布了一段时间,但 Ubuntu 并没有在上一个版本更新 (Ubuntu 21.04) 中使用它,原因是团队希望仔细打磨之后再引入,因此Ubuntu 21.04 跳过了最新的 GNOME 40。 GNOME 40 主要新变化包括:引入新的布局设计(新的 Activities Overview)、引入水平工作区、迷你工作区缩略图(可交互)、新的水平滚动应用启动器,以及当概览或应用启动器处于活动状态时消失的顶栏等等。 和上游的 GNOME 40 一样,当用户初始登录到 Ubuntu 21.10 时,也会看到一个新的“概览”,这是为了让用户适应新的工作方式。 在当前的每日构建版中,Ubuntu 21.10 将 Dock 栏放在了左侧。他们没有将 Dock 栏移动至底部,也没有恢复使用 GNOME 的原生 Dock 栏。 目前来看,仍存在一些视觉上不一致的问题,例如工作区没有阴影、“顶栏”颜色没和 Dock 栏的保持一致(当顶栏在概览中变成不可见时,会使 Dock 栏看起来像被切断)。 当然,目前...
- 下一篇
formDesigner 1.12.0 发布,拖拽式表单设计器
formDesigner 1.12.0 已经发布,拖拽式表单设计器。 此版本更新内容包括: 文本输入框增加正则验证,内置部分通用验证 日期、时间增加范围选择 增加文本框的复合输入框 修复日期选择后值格式不正确的bug 优化代码结构,扩展更容易,阅读更简单 详情查看:https://gitee.com/wurong19870715/formDesigner/releases/1.12.0
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS关闭SELinux安全模块
- CentOS8安装Docker,最新的服务器搭配容器使用
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- Hadoop3单机部署,实现最简伪集群
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Windows10,CentOS7,CentOS8安装Nodejs环境
- 设置Eclipse缩进为4个空格,增强代码规范