Fork me on GitHub

升級至 Swift 3

Xcode 8.0 內建 Swift 升級工具,可以幫助您將專案更新到 Swift 3,或者更新到 Swift 2.3 來使用新的 SDKs。

升級前的準備

為了讓更新更有效率,請確保您打算更新的專案使用 Xcode 7.3.1 時能成功建置,並且通過所有測試。
另外,還要確認專案有使用代碼管理系統(source control system)。讓你能輕鬆地審查升級工具做的修改,有必要的話也能復原再重試一次。

如果您使用多個 schemes 來建置多個獨立產品(或針對不同平台的同一產品),你需要建立一個 scheme 來建置專案中所有東西,包含所有的平台還有測試目標。升級工具會先使用你選擇的 scheme 來做一次升級建置來確定需要修改的地方。所以只有scheme 內的目標會被處理。
要檢查和修改 scheme 的內容,點選Edit Scheme…,然後從左邊的選單選擇Build 分頁來確認所有的目標和測試目標都已包含在內。

如果你的專案使用了 Carthage 或 CocoaPods,請參考使用 Carthage/CocoaPods 段落

Swift 升級工具

當你第一次使用 Xcode 8.0 打開專案時,會啟動升級工具來進行升級。你也可以從選單中的 Edit -> Convert -> To Current Swift Syntax… 手動開啟工具。

有兩種升級的選項:

你也可以先升級到 Swift 2.3, 之後再用升級工具升級到 Swift 3。

執行工具並從 “Use Swift 2.3”“Use Swift 3” 之中選擇一個之後,會列出所有會升級的目標。不含 Swift 程式碼的目標不會被列出來。

‘Next’ 會帶出 ‘Generate Preview’ 頁面,工具也會進行一次建置來收集需要修改的地方。完成後會顯示所有會進行的修改。在顯示差異模式中,左邊是轉換前的檔案,右邊是轉換後的檔案。點 'Save' 後就會修改檔案。如果你選的是升級到 Swift 2.3 ,會在設定檔中加入 “Use Legacy Swift” 的設定。

如果升級時遇到問題。切換到“Report Navigator”,點選出現的“Convert”項目,裡面會有轉換的 Log,可以用它來找出問題所在。

如果你看到和 code-sign 有關的錯誤,把 code-signing 關掉後再試一次。
如果您看到其他錯誤,請在https://bugreport.apple.com 提交一個 bug 報告,包括錯誤的細節。

如果你想要修改原來的程式來避開錯誤,回復程式到使用升級工具前的狀態,修改程式之後,再手動執行一次升級工具。

Swift 3 修改簡介

修改工具會協助你修改 Swift 3 的許多重大改變。你可以在 https://github.com/apple/swift-evolution 看到 Swift 3 修改的提案大鋼。

以下是會讓程式碼不相容的重大修改:

API設計指南

Objective-C API 會遵照新的 雨燕API設計指南 來匯入。這會同時影響SDK 和使用者的 Objective-C 框架。Swift 標準函式庫也做了許多對應的修改。SE-0005 - Better Translation of Objective-C APIs Into Swift 有更多的細節。
升級工具會把 enum 改成小寫開頭來遵照指南。

SDK

部分的框架,像是 CoreGraphics, Dispatch 和 Foundation 中的全域變數和函式將會變為成員函式和屬性。詳情請參閱 SE-0044 - Import as member, SE-0088 - Modernize libdispatch for Swift 3 naming conventions.

Swift 3 會刪除 Foundation 類別的 NS 前輟 ,請參閱SE-0086 -掛斷NS前綴斯威夫特基金會

Swift 標準函式庫

Collection Indexing 在 Swift 3 有很大的改變,詳見 SE-0065 - A New Model for Collections and Indices.最明顯的改變是,Index 不再有successor() predecessor() advancedBy(_:)advancedBy(_:limit:) ,或distanceTo(_:)方法。這些方法會移到 Collection 上,Collection 現在會負責 Index 的移動。

myIndex.successor()  =>  myCollection.index(after: myIndex)
myIndex.predecessor()  =>  myCollection.index(before: myIndex)
myIndex.advance(by: …) => myCollection.index(myIndex, offsetBy: …)
  

如果升級工具不知道負責Collection,它會插入一個 Placeholder ,你必須手動改成你的Collection 。

Range 型別也做了一些修改。以前x..<yx...y會產生同一種型別, Range<T>。現在,他會產生四種型別其中一種: RangeCountableRangeClosedRangeCountableClosedRange 。我們把Range切割成RangeClosedRange型別,以允許封閉範圍包括該類型的最大值(例如 0...Int8.max )。普通的 Range 和 Countable 差在他們的功能。

..<...會試著會傳最通用的的 Range,讓for i in 1..<10產生 CountableRange 來確保你的舊程式繼續運作。如果你有一個 Range 物件想要轉換成另一種型別的 Range 物件好傳進其他函數,你可以用 Range 型別的建構子來作轉換。

var r = 0..<10 // CountableRange<Int>
Range(r) // converts to Range<Int>
  

Language

After Migration

While the migrator will take care of many mechanical changes for you, it is likely that you will need to make more manual changes to be able to build the project after applying the migrator changes.

You may see compiler errors that have associated fixits; while the migrator is designed to incorporate fixits that the Swift 3 compiler provides, it is a known limitation that this is not guaranteed to work 100% (particularly when you have inter-dependencies between targets) and some fixits may be missed.

Even if it compiles fine, the code that the migrator provided may not be ‘ideal’, for example you may see casts to ‘NS’ prefixed types (url as NSRL), that would be better if the code was restructured to use related APIs on the new URL value type instead. You may also see new comments that the migrator added (/*Migrator FIXME: ...*/) where it provides a hint on how to convert the code better.

See Known Migration Issues section, for a list of issues that you may encounter while trying to migrate your project.

Using Carthage/CocoaPods Projects

If you are using binary Swift modules from other projects that are not built along with your project in your Xcode workspace, you can choose from one of the following migration strategies:

Known Migration Issues

Swift Standard Library

SDK

static let MyGreatNotification = Notification.Name("MyGreatNotification")

// Use site (no change)
NotificationCenter.default().post(name: MyController.MyGreatNotification, object: self)'

Swift 3 Language

Miscellaneous