作为一般的经验法则,苹果在某个版本的iOS中引入的所有API和系统功能只能在针对该特定版本或任何后续版本时使用。
然而,今年有几个非常有趣的例外,即iOS 15中引入的某些SwiftUI API实际上可以一直使用和部署到iOS 13。让我们来看看其中的一些API是什么,以及它们是如何做到完全向后兼容的。
静态样式
今年一个非常整洁的语法改进是,我们现在可以使用静态API来引用SwiftUI的各种造型协议的实例,而不是明确地实例化这些值。例如,以下是在将ListStyle 应用到 SwiftUIList 时使用新旧语法的前后对比。
// Before:
List(items) { item in
...
}
.listStyle(GroupedListStyle())
// After:
List(items) { item in
...
}
.listStyle(.grouped)
上面使用的是一个新的Swift语言特性,它使我们能够定义静态的协议API,可以用来创建符合该协议的实例(详见本文)。但是,为什么苹果公司能够使SwiftUI对该新语言特性的实现向后兼容早期的操作系统版本呢?
始终发射到客户端
这就是一个相对较新的属性--_alwaysEmitIntoClient ,它使框架作者能够告诉Swift编译器,将一个给定的函数、计算属性或下标的实现排放到正在消费该框架的二进制文件中。这个新属性是Swift*库进化*工作的一部分,旨在提供一套工具,以便随着时间的推移顺利进化框架和库API。
虽然这仍然是一个下划线属性(在苹果API设计的世界里,这基本上意味着 "技术上是公开的,但不应该被认为是公开的"),但它使我们有可能在旧的操作系统版本上使用上述新的造型API--因为通过用@_alwaysEmitIntoClient 注释这些API,苹果告诉编译器将这些API的实现嵌入到我们的应用二进制中,而不是链接到由操作系统提供的实现。
可绑定的列表元素
另一个完全向后兼容的新List (和ForEach )功能是我们现在可以创建包含对正在渲染的集合元素的引用的列表Binding 。
以前,这需要相当多的代码来可靠地实现,但现在,我们可以使用与引用其他绑定时相同的$-prefixing语法来创建可绑定的列表元素--像这样:
struct TodoList: View {
@Binding var items: [Item]
var body: some View {
List($items) { $item in
TextField("Item", text: $item.text)
}
}
}
很好!就像我们之前看的造型 API 一样,由于新的 Swift 编译器能力和@_alwaysEmitIntoClient 注释的结合,上述新功能是向后兼容的。
用视图构建器闭包替换单视图参数
最后,让我们看看在创建某些内置视图时,我们现在如何能够充分使用 SwiftUI 的ViewBuilder API 的力量,这些视图以前需要我们将其基于View 的一些参数作为单独的值传递。例如,以下是我们现在如何在为NavigationLink 创建目标时使用ViewBuilder 封闭。
// Before:
NavigationLink(
article.title,
destination: ArticleView(article: article)
)
// After:
NavigationLink(article.title) {
ArticleView(article: article)
}
上述新的基于闭包的API不仅可以使我们的代码更容易阅读(特别是当我们希望将修改器直接应用于作为参数传递的视图时),而且它还可以使我们在计算参数时使用任何ViewBuilder-兼容的表达式。例如,现在我们可以很容易地为一个导航链接的目的地返回不同的视图,这取决于它将代表什么数据。
NavigationLink(article.title) {
if article.isFeatured {
FeaturedArticleView(article: article)
} else {
ArticleView(article: article)
}
}
其他升级了类似的、完全向后兼容的API的视图包括:Section (它现在接受ViewBuilder- powered closures for itsheader andfooter arguments),以及Picker (它现在允许我们在构建其label 时使用一个closure)。
总结
我真的很高兴看到苹果公司开始探索使某些新的API向后兼容早期操作系统版本的想法,因为这让我们第三方开发者能够利用这些新的系统功能,而不需要我们增加最小部署目标,或使用可用性检查。
当然,这些向后兼容的API并不包括任何全新的组件,或任何新的面向用户的功能--但就像大多数新的Swift语言功能使我们能够在不破坏向后兼容性的情况下发展我们的代码一样,这些新的API应该让我们以某种显著的方式改进我们基于SwiftUI的代码--特别是在处理可绑定的列表元素时。
谢谢你的阅读!