【译】Swift API可用性

86 阅读10分钟

原文链接 Swift API Availability

代码存在于一个无限丰富的世界。你能想象的一切都是自愿的——只要你知道如何表达你的欲望。

作为开发人员,我们知道代码最终会被编译成软件,并被迫在现实世界中争夺稀缺的硬件资源。尽管在那之前,我们可以尽情享受无限理想主义的感觉……嗯,大部分是。因为软件不是一门纯粹的科学,我们的工作——实际上——只不过是在漏洞百出的抽象之间穿梭于破碎的管道中。

本周在NS Hipster上,我们正在探索我们乏味工作的一个典型方面:应用编程接口可用性。好消息是,Swift提供了一流的构造,可以通过“可用”和“可用”来处理这些现实世界的约束。然而,这些语言特性有一些细微差别,许多Swift开发人员对此并不了解。所以一定要继续阅读,确保你清楚所有可用的选项。

@可用

在Swift中,您使用“可用”属性用可用性信息注释API,例如“此API在mac OS 10.15中已弃用”或“此API需要Swift 5.1或更高版本”。有了这些信息,编译器可以确保应用使用的任何此类API可用于当前目标支持的所有平台。

“可用”属性可以应用于声明,包括顶级函数、常量和变量、结构、类、枚举和协议等类型以及类型成员——初始化器、类去初始化器、方法、属性和下标。

平台可用性

当用于指定API的平台可用性时,@可用属性可以采用一种或两种形式:

  • 列出多个平台最低版本要求的“速记规范”

  • 一个扩展的规范,可以传达关于单个平台可用性的其他细节

速记规范

@available(platform version , platform version ..., *)
  • 一个平台;ios、mac Catalyst、mac OS/OS X、tvOS或watch OS,或任何附加了应用扩展的平台*(例如mac OS*应用扩展)。

  • 由一个、两个或三个正整数组成的版本号,中间隔着一个 (.), 表示主**要、次要和**补丁版本的句号。

  • 逗号分隔的 (,) 列表中的零个或多个版本化平台。

  • 星号 (), 表示API可用于所有其他平台。平台可用性注释总是需要星号来处理潜在的未来平台(例如****传闻已久的iDi ***shw asherOS)。

例如,**WWDC 2019**推出的新的跨平台API可能会注释为:

@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)

速记规范是注释平台可用性的一种方便方法。但是,如果您需要传达更多信息,例如何时或为什么不建议使用API,或者应该使用什么作为替代,您将选择扩展规范。

引入、弃用、过时和不可用

// With introduced, deprecated, and/or obsoleted@available(platform | *          , introduced: version , deprecated: version , obsoleted: version          , renamed: "..."          , message: "...")// With unavailable@available(platform | *, unavailable , renamed: "..." , message: "...")
  • 一个平台,和以前一样,或者所有平台的星号 (*) 。

  • 要么引入,要么弃用,要么/or废弃…

  • 一个引入的版本,表示API可用的第一个版本

  • 不建议使用的版本,表示使用API时生成编译器警告的第一个版本

  • 一个过时的版本,表示当使用API时第一个版本会产生编译器错误

  • …或不可用,这导致应用编程接口在使用时生成编译器错误

  • 用另一个应用编程接口的密钥路径重命名;当提供时,Xcode提供了自动“修复”

  • 编译器警告或错误中包含的消息字符串

与速记规范不同,此表单只允许指定一个平台。因此,如果您想注释多个平台的可用性,您需要堆栈@可用属性。例如,下面是前面的速记示例如何用多个属性表示:

@available(macOS, introduced: 10.15)@available(iOS, introduced: 13)@available(watchOS, introduced: 6)@available(tvOS, introduced: 13)

Apple SDK框架广泛使用可用性注释,在ios、mac OS和其他平台的每个版本中指定新的和已弃用的API。

例如,ios 13引入了一个新的**UI Colle ction View Composition **Layout类。如果您通过按住命令(⌘)并在Xcode中单击该符号跳转到它的声明,您将看到它被注释为“可用”:

@available(iOS 13.0, *)open class UICollectionViewCompositionalLayout : UICollectionViewLayout { … }

这个“可用”属性告诉编译器,UI Colle ction View Composition Layout只能在运行ios、13.0版或更高版本的设备上调用*(有警告;请参见下面的注释*)。

如果您的应用程序只针对ios 13,那么您可以使用UI Colle ction View Composition Layout而无需任何特殊考虑。但是,如果您的部署目标设置在ios 13以下,以便支持以前版本的ios(许多现有应用程序都是如此),则必须对UI Colle ction View Composition Layout的任何使用进行条件化。

稍后再详细介绍。

Swift语言可用性

您的代码可能依赖于Swift的新语言功能,如属性**包装器或默认枚举大小写关联**值——这在Swift 5.1中都是新的。如果你想支持你的应用程序与以前版本的Xcode的开发,或者让你的Swift包为多个Swift编译器工具链工作,你可以使用@可用属性来注释包含新语言功能的声明。

当用于指定API的Swift语言可用性时,@可用属性采用以下形式:

@available(swift version)

与平台可用性不同,Swift语言版本注释不需要星号 (*); 编译器,有一种Swift语言,有多个版本。

#可用

在Swift中,您可以使用可用性条件#可用来谓词if、Guard和time语句,以确定API在运行时的可用性。与“可用”属性不同,#可用条件不能用于Swift语言版本检查。

可用表达式的语法类似于可用属性的语法:

if | guard | while #available(platform version , platform version ..., *) 

现在我们知道了如何为可用性注释API,让我们看看如何基于我们的平台和/orSwift语言版本注释和条件化代码。

使用不可用的API

与Swift中**必须处理或传播抛出的错误类似,使用可能不可用的**API必须是带注释或条件化的代码。

当您试图调用至少一个受支持目标不可用的API时,Xcode将推荐以下选项:

  • “添加if#可用版本检查”

  • “向封闭声明添加@可用属性”(在每个封闭范围中建议;例如,当前方法以及该方法包含的类

按照我们对错误处理的类比,第一个选项类似于函数调用的预先尝试,第二个选项类似于在do/catch.中包装语句

例如,在一个支持ios 12和ios 13的应用程序中,子类UI Colle ction View Composition Layout的声明必须用可用注释,对该子类的任何引用都需要用可用#进行条件化:

@available(iOS 13.0, *)final class CustomCompositionalLayout: UICollectionViewCompositionalLayout {  }func createLayout() -> UICollectionViewLayout {    if #available(iOS 13, *) {        return CustomCompositionalLayout()    } else {        return UICollectionViewFlowLayout()    }}

Swift包含许多相互依赖的概念,并且由于语言的各个部分相互作用,像可用性这样的横切问题通常会导致显著的复杂性。例如,如果您创建了一个子类,它覆盖了一个被其超类标记为不可用的属性,会发生什么?或者,如果你试图调用一个在一个平台上重命名,但在另一个平台上被运算符取代的函数呢?

虽然在这里列举每一个具体行为都很乏味,但这些问题可能也经常出现在现实世界中开发应用的混乱过程中。如果你发现自己在想“如果”,厌倦了反复试验,你可以试着咨询**Swift语言测试套件**,以确定预期的行为。

或者,你可以从**Clang的诊断文本**中推测事情是如何运作的:

诊断文本

warning:‘unavailable’ availability overrides all other availability informationwarning:unknown platform A in availability macrowarning:feature cannot be introduceddeprecatedobsoletedin B version C before it wasintroduceddeprecatedobsoletedin version E ; attribute ignoredwarning:use same version number separators ‘_’ or ‘.’; as in ‘major[.minor[.subminor]]’warning:availability does not match previous declarationwarning: overridingmethodintroduced afterdeprecated beforeobsoleted beforethe protocol method it implementsoverridden methodon B ( C vs. D )warning: overridingmethod cannot be unavailable on A when the protocol method it implementsits overridden method is available

在自己的API中注释可用性

尽管作为苹果应用编程接口的消费者,你会经常与@可用性互动,但你不太可能将它们用作应用编程接口的生产者。

应用程序中的可用性

在应用程序的上下文中,使用@可用弃用警告在使用视图控制器、方便方法或不再建议使用的团队之间进行通信可能会很方便。

@available(iOS, deprecated: 13, renamed: "NewAndImprovedViewController")class OldViewController: UIViewController { … }class NewAndImprovedViewController: UIViewController { … }

然而,使用不可用或已弃用的API对应用程序来说用处不大;如果不期望在该上下文之外销售API,则可以直接删除API。

第三方框架中的可用性

如果您维护的框架在某种程度上依赖于Apple SDK,您可能需要根据底层系统调用的可用性注释您的**API**。

然而,如果您的框架以不公开此类实现细节的方式包装SDK功能,您可以选择加入新功能,而不影响您自己的应用编程接口的可用性。例如,将功能委托给NS Llangua**ge Tagger的NLP库可以在可用时使用自然语言框架**(由#可用决定),而无需任何用户可见的应用编程接口更改。

Swift软件包中的可用性

如果您以与平台无关的方式编写Swift qua Swift,并将该代码作为Swift包分发,您可能希望使用“可用”向消费者提醒即将淘汰的API。

不幸的是,目前没有办法根据库版本指定弃用(平台列表由编译器硬编码)。虽然这有点像黑客,但你可以通过指定一个过时的/不存在的Swift语言版本来表达弃用:

@available(swift, deprecated: 0.0.1, message: "Deprecated in 1.2.0")func going(going: Gone...) {}

处理弃用警告

正如我们中的一些人敏锐地意识到的那样,目前不可能在Swift中压制贬低警告。而在目标C中,您可以用#pragma clang诊断推送来抑制警告/忽略/,但Swift没有这样的便利。

如果你是开启了“硬模式”的l33 t程序员之一(又名“将警告视为错误”)SWIFT_TREAT_WARNINGS_AS_ERRORS),但是发现自己被弃用警告所阻碍,这里有一个你可以使用的作弊代码:

class CustomView {    @available(iOS, introduced: 10, deprecated: 13, message: "😪")    func method() {}}CustomView().method() // "'method()' was deprecated in iOS 13: 😪"protocol IgnoringMethodDeprecation {    func method()}extension CustomView: IgnoringMethodDeprecation {}(CustomView() as IgnoringMethodDeprecation).method() // No warning! 😁

作为一种职业,编程是信息时代后工业化世界经济的后稀缺理想的缩影。但是,即使我们远离了物理限制,我们仍然天生受到我们无法控制的力量的限制。然而,通过仔细和深思熟虑的练习,我们可以学会利用我们可以利用的一切。