迁移到 Swift 4
这是一份针对 Xcode 9 和从 Swift 3 迁移的旧文档。
Xcode 9.0 配备了 Swift 迁移工具,可帮助您将项目迁移到 Swift 4。
迁移前准备
确保您要迁移的项目在 Swift 3.2 模式下成功构建,并且所有测试均通过。请记住,Swift 3.2 与 3.1 以及您构建时所用的 SDK 相比,确实有重大更改,因此您可能需要先解决错误。
强烈建议在源代码控制下管理您的项目。这将使您能够轻松查看通过迁移助手应用的更改,并在需要时放弃它们并重新尝试迁移。
与去年不同,迁移器直接内置于编译器中,而不是一个单独的工具,因此它可以平等地理解 Swift 3.2 和 Swift 4 代码,并将它们编译在一起,就像 Swift 4 编译器一样。这意味着您可以决定何时以及是否要根据项目的实际情况,按目标进行迁移。虽然绝对鼓励迁移到 Swift 4,但这并非一个全有或全无的过程,因为 Swift 3.2 和 Swift 4 目标可以共存并链接在一起。
迁移助手执行迁移器构建以收集更改,使用您选择的 scheme,因此将要处理的目标是 scheme 中包含的目标。要查看和修改 scheme 中包含的内容,请调用编辑 Scheme… 工作表,然后从左侧的列中选择构建选项卡,并确保包含所有目标及其单元测试。
如果您的项目依赖于 Carthage 或 CocoaPods 提供的其他开源项目,请查阅 使用 Carthage/CocoaPods 项目 部分。
Swift 迁移助手
当您首次使用 Xcode 9 打开项目时,您将在 Issue Navigator 中看到一个迁移机会项:单击它以激活一个工作表,询问您是否要迁移。您可以稍后提醒,或从菜单Edit -> Convert -> To Current Swift Syntax…手动调用迁移器。
您将看到要迁移的目标列表。不包含任何 Swift 代码的目标将不会被选中。
今年只有一个迁移工作流程,尽管在两种@objc
推断之间有一个选择
- 最小化推断:仅在基于静态推断需要时,才向代码添加 @objc 属性。使用此选项后,您需要按照完成 Swift 4 最小化推断迁移中的手动步骤来完成转换。
- 匹配 Swift 3 行为:在代码中任何会被编译器隐式推断的地方添加 @objc 属性。此选项不会更改二进制文件的大小,因为它在 Swift 3 隐式添加 @objc 属性的任何地方都显式添加了它们。
有关这两种选择的更多信息和影响,请参阅 Xcode 帮助文章 迁移到 Swift 4
@objc
推断。
单击下一步将弹出生成预览工作表,助手将启动迁移构建以获取源代码更改。完成后,您将看到一旦单击“保存”后将应用的所有更改。 这还将把迁移目标的Swift Language Version构建设置更改为 Swift 4。
处理目标时可能存在问题,这将对迁移过程产生负面影响。切换到Report Navigator并选择添加的Convert条目; 这是转换构建日志。检查日志中可能出现的错误。
如果您看到关于无法对目标进行代码签名的错误,请尝试从目标的构建设置中禁用代码签名。如果您看到其他错误,请提交错误报告 并包含详细信息。强烈建议您附加一个可以说明错误迁移的项目(如果可能)。
Swift 4 迁移更改概述
迁移器建议的大部分更改来自对先前 SDK 和当前 SDK 的比较生成的数据,这可能会导致标识符和类型重命名,例如;以及来自正常的编译器修复。 在一些特殊情况下,迁移器可以安全地执行直接的机械更改。
SDK 更改
两个最普遍的 SDK 更改是将全局常量移动到静态类型属性中,并将字符串常量转换为 Swift 枚举案例。这些由迁移器自动处理。您还将看到各种类型签名更改。
值得注意的特殊情况
新字符串
String
在 Swift 4 中有新的 API,其中一些现在返回 Substring
或 String
。为了简化此过渡,当 API 现在期望不同的类型时,迁移器将插入显式初始化器转换。
有关新的
String
和Substring
API 的更多信息,请参阅 SE-0163 的 Swift 演进文档。
SE-0110:区分单元组和多参数函数类型
f: (Void) -> ()
当使用 f: (Void) -> ()
作为函数参数的类型时,通常意味着它是 f: () -> ()
,因此迁移器会建议您改用此类型。 否则,根据 Swift 4 中 SE-0110 的新规则,您需要将函数 f
称为 f(())
。
添加元组解构
对于如下代码:
func foo(_: ((Int, Int) -> ()) {}
foo { (x, y) in print(x + y) }
迁移器必须添加显式元组解构才能继续在 Swift 4 中构建,例如:
func foo(_: ((Int, Int) -> ()) {}
foo { let (x, y) = $0; print(x + y) }
有关此语言更改的更多信息,请参阅 SE-0110 的 Swift 演进文档。
默认参数值必须是 public
编译器现在对用作公共函数的默认参数的引用的非字面量值的可访问性更加严格; 它们也必须是 public
。 除此之外,这还为优化对调用站点值的访问提供了机会。 因为这可能涉及 API 设计,所以迁移器不建议修复,尽管作为 API 作者,您可以考虑一些可能性
- 使引用的默认值
public
。 - 提供返回合理默认值的
public
函数。
在这两种情况下,请考虑公开新 API 的影响,确保记录您的公共符号。
迁移后
虽然迁移器会为您处理许多机械更改,但在应用迁移器更改后,您可能需要进行更多手动更改才能构建项目。
您可能会看到与修复相关的编译器错误; 虽然迁移器旨在合并 Swift 4 编译器提供的修复,但如果某些修复不是 100% 适用,则可能不会应用它们。
即使编译正常,迁移器提供的代码也可能不是理想的。 使用您的最佳判断并检查更改是否适合您的项目。
已知迁移问题
有关 Xcode 9 中迁移的最新已知问题的信息,请参阅 开发者下载页面上 Xcode 9 的发行说明的Swift 迁移部分。
使用 Carthage/CocoaPods 项目
以下是在使用 Carthage、CocoaPods 或 Swift Package Manager 等包管理器迁移具有外部依赖项的项目时需要考虑的一些重要事项。
- 建议使用源代码依赖项而不是二进制 Swift 模块,因为 Swift 3.1 模块将与 Swift 3.2/4 模块不兼容,除非您可以获得以 Swift 3.2 或 Swift 4 模式构建的发行版。
- 确保您的源代码依赖项和您自己的目标在 Swift 3.2 模式下都能成功构建。
- 如果您已设置框架搜索路径以查找 Carthage 构建文件夹内的二进制 Swift 模块,请删除搜索路径或清理构建文件夹,以确保您仅使用从 Xcode 工作区构建的 Swift 模块。
- 只要您的源代码依赖项可以在 Swift 3.2 模式下构建,就没有必要迁移它们。
其他
- 如果您的项目中有多个 scheme 涵盖不同的目标,您只会收到需要迁移其中一个的通知。 您需要手动选择新的 scheme,然后运行 Edit -> Convert -> To Current Swift Syntax 以迁移剩余的 scheme。 或者,您可以创建一个包含项目中所有目标的 scheme,并在运行迁移助手之前选择它。