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 段落
當你第一次使用 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 的許多重大改變。你可以在 https://github.com/apple/swift-evolution 看到 Swift 3 修改的提案大鋼。
以下是會讓程式碼不相容的重大修改:
Objective-C API 會遵照新的 雨燕API設計指南 來匯入。這會同時影響SDK 和使用者的 Objective-C 框架。Swift 標準函式庫也做了許多對應的修改。SE-0005 - Better Translation of Objective-C APIs Into Swift 有更多的細節。
升級工具會把 enum 改成小寫開頭來遵照指南。
部分的框架,像是 CoreGraphics, Dispatch 和 Foundation 中的全域變數和函式將會變為成員函式和屬性。詳情請參閱 SE-0044 - Import as member, SE-0088 - Modernize libdispatch for Swift 3 naming conventions.
Swift 3 會刪除 Foundation 類別的 NS 前輟 ,請參閱SE-0086 -掛斷NS前綴斯威夫特基金會 。
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..<y
和x...y
會產生同一種型別, Range<T>
。現在,他會產生四種型別其中一種: Range
, CountableRange
, ClosedRange
, CountableClosedRange
。我們把Range
切割成Range
和ClosedRange
型別,以允許封閉範圍包括該類型的最大值(例如 0...Int8.max
)。普通的 Range 和 Countable 差在他們的功能。
Range<Bound>
和ClosedRange<Bound>
現在只需要在邊緣處符合Comparable
。這讓您可以一個產生一個Range<String>
。Range
和ClosedRange
不能被迭代(iterate)(它們不是集合了),因為Comparable
不支援尋找下一個元素。CountableRange
和CountableClosedRange
的邊緣需要支援Strideabe
,他們支援Collection
,所以可以對它們進行迭代。該..<
和...
會試著會傳最通用的的 Range,讓for i in 1..<10
產生 CountableRange
來確保你的舊程式繼續運作。如果你有一個 Range 物件想要轉換成另一種型別的 Range 物件好傳進其他函數,你可以用 Range 型別的建構子來作轉換。
var r = 0..<10 // CountableRange<Int>
Range(r) // converts to Range<Int>
Consistent first argument labels
The first argument label in functions is now considered API by default, see SE-0046 - Establish consistent label behavior across all parameters including first labels.
The migrator adds underscore labels to preserve the existing APIs:
func foo(bar: Int) => func foo(_ bar: Int)
Changes with handling of UnsafePointer<T>
In Swift 3, the nullability of non-object pointer types is now represented explicitly using optionals, such as UnsafePointer<Int>?
, see SE-0055 - Make unsafe pointer nullability explicit using Optional. This means that the types UnsafePointer
, UnsafeMutablePointer
, AutoreleasingUnsafeMutablePointer
, OpaquePointer
, Selector
, and NSZone
now represent non-nullable pointers, i.e. pointers that are never nil
. Code working with these types may have to make several changes:
nil
, it must be optional. The migrator will handle some simple cases here, but in general you must decide whether your pointers should be optional just like your object references.pointee
property (formerly memory
) or subscript elements. Optional chaining syntax works well here, e.g. result?.pointee = sum
.Optional
.Int(bitPattern: nullablePointer)
.If an Objective-C generic class is used in a checked as?
, as!
, or is
cast, the generic parameters are not checked at runtime. The cast succeeds if the operand is an instance of the Objective-C class, regardless of parameters.
let x = NSFoo<NSNumber>(value: NSNumber(integer: 0))
let y: AnyObject = x
let z = y as! NSFoo<NSString> // Succeeds
Swift subclasses can only inherit an Objective-C generic class if its generic parameters are fully specified.
// Error: Can't inherit Objective-C generic class with unbound parameter T
class SwiftFoo1<T>: NSFoo<T> { }
// OK: Can inherit Objective-C generic class with specific parameters
class SwiftFoo2<T>: NSFoo<NSString> { }
Swift can extend Objective-C generic classes, but the extensions cannot be constrained, and definitions inside the extension do not have access to the class’s generic parameters.
extension NSFoo {
// Error: Can't access generic param T
func foo() -> T {
return T()
}
}
// Error: extension can't be constrained
extension NSFoo where T: NSString {
}
Foundation container classes NS[Mutable]Array
, NS[Mutable]Set
, and NS[Mutable]Dictionary
are still imported as nongeneric classes for the time being.
Objective-C id is imported as as Swift Any type
SE-0116 - Import Objective-C id as Swift Any type
Objective-C interfaces that use id
and untyped collections will be imported into Swift as taking the Any
type instead of AnyObject
.
Changes with handling of ImplicitlyUnwrappedOptional
SE-0054 - Abolish ImplicitlyUnwrappedOptional type
Variable bindings which previously had inferred type T!
from their binding on the right-hand side will now have type T?
. The compiler will emit an error at sites where those bound variables are used in a context that demands a non-optional type and suggest that the value be forced with the !
operator.
Explicitly written nested IUO types (like [Int!]
) will have to be rewritten to use the corresponding optional type ([Int?]
) or non-optional type ([Int]
) depending on what’s more appropriate for the context. However, most declarations with non-nested IUO type will continue to work as they did before.
Unsugared use of the ImplicitlyUnwrappedOptional
type will have to be replaced with the postfix !
notation.
Closures are non-escaping by default
SE-0103 - Make non-escaping closures the default
The default for closures was switched and they require an @escaping
annotation if a closure argument can escape the function body.
Unsafe[Mutable]RawPointer
type has been introduced. It replaces Unsafe[Mutable]Pointer<Void>
. Conversion from UnsafePointer<T>
to UnsafePointer<U>
has been disallowed. Unsafe[Mutable]RawPointer
provides an API for untyped memory access and an API for binding memory to a type. Binding memory allows for safe conversion between pointer types.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.
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:
Include the source code of the project in your Xcode workspace
With this approach you will build and migrate the open-source project along with your own project. Use Xcode 7.3[.1] to make the necessary changes and validate that the project builds and links everything correctly. Include the other Xcode project files in your workspace and setup your scheme for building the targets that your project depends on. If you have setup framework search paths for finding the binary Swift modules inside Carthage’s build folder, either remove the search paths or clean the build folder, so that you are sure that you are only using the Swift modules that are built from your Xcode workspace.
Wait until the upstream open-source project updates to Swift 2.3 or Swift 3
You can follow this workflow for migrating your project:
SetIndex
and DictionaryIndex
.
index.successor()
migrates to Collection.index(after: index)
index.predecessor()
migrates to Collection.index(before: index)
index.advancedBy(delta)
migrates to Collection.index(index, offsetBy: delta)
index.advancedBy(delta, limit: otherIndex)
migrates to Collection.index(index, offsetBy: delta, limitedBy: otherIndex)
index.distanceTo(otherIndex)
migrates to Collection.distance(from: index, to: otherIndex)
Unmanaged
type had a static method fromOpaque(_:)
and an instance method toOpaque()
, which converted the unmanaged reference from and to the COpaquePointer
type. In Swift 3 these have been changed to convert from an UnsafePointer<Void>
and to an UnsafeMutablePointer<Void>
to match the common use of being passed as the “context pointer” for a C API. In most cases, you will be able to simply remove uses of COpaquePointer
(now renamed to OpaquePointer
).Collection
, implement the method func index(after: Index) -> Index
. For a BidirectionalCollection
, also implement func index(before: Index) -> Index
.RandomAccessCollection
, you should also implement func index(_: Index, offsetBy: Int) -> Index
and func distance(from: Index, to: Index) -> IndexDistance
.Range
formed from the half-open range operator (e.g. 1..<2
) that is used as Sequence
(e.g. in a for-in
loop), you might see an error like **“type ‘RangeCountableRange
.Collection.Index.Distance
to Collection.IndexDistance
(no dot)index
to offset
when accessing the result of Collection.enumerated()
Range<Index>
does not conform to protocol Sequence
after migrating a range of indices, use the collection’s indices
property.
for _ in str.startIndex..<someIndex {}
–> for _ in str.indices[str.startIndex..<someIndex] {}
Zip2Sequence(_:_:)
has been removed; use the free function zip(_:_:)
instead.min
/max
inside extensions to Collection
can cause collisions with Collection
’s native methods; add Swift
. before min
/max
to resolve the issue.Selector()
should be migrated to nil
.Range<>.reversed
got removed; to simulate its functionality, users can call <Collection>[<Range>].indices.reversed()
.func foo<C: CollectionType where C.Index: BidirectionalIndexType>() {}
should migrate to func foo<C: BidirectionalCollection>() {}
but instead it migrates to func foo<C: Collection where C.Index: BidirectionalIndex>() {}
Notification.Name
type. Since, it’s common for notification names and other string constants to be declared globally or as static members, the best way to take advantage of these new types is usually to construct the wrapper at the point of declaration:static let MyGreatNotification = Notification.Name("MyGreatNotification")
// Use site (no change)
NotificationCenter.default().post(name: MyController.MyGreatNotification, object: self)'
FileAttributeKey
is another of the “stringly-typed” APIs that have been changed to use struct “wrapper types”. When such types are used with dictionaries (such as the result of FileManager
’s attributesOfItem(atPath:)
method), the string value will usually need to be extracted with the rawValue
property.
let mtime = try FileManager.default().attributesOfItem(atPath: "/")[FileAttributeKey.size.rawValue] as? NSNumber
NSURL
to the new value type URL
. However, there are certain methods on NSURL
, like checkResourceIsReachableAndReturnError
, that produce errors through an out-parameter instead of using Swift’s error-handling mechanism. The corresponding method on URL
, checkResourceIsReachable
, uses the error-handling mechanism as expected.&26613405
NSURL
methods; you will need to manually update your code if you want to use the new APIs on the URL
value type. For the common pattern of treating an error as unreachable, you can use try?
: let isReachable = (try? resourceURL.checkResourceIsReachable()) ?? false)
- (Note that for this particular API it’s recommended you switch to URL’s resourceValues(forKeys:)
, which handles the casting for you.)port
property on NSURL
produces an optional NSNumber
, while the corresponding property on URL
is an optional Int
. The Swift 3 migrator is conservative and will continue using the NSURL
property; you will need to manually update your code if you want to use the new API.NSData
to the new value type Data
. However, there are certain methods on NSData
that operate on UnsafeMutablePointer<Void>
, while the corresponding methods on Data
use UnsafeMutablePointer<UInt8>
. (For example, NSData.getBytes(_:length:)
is more accepting than Data.copyBytes(_:length:)
.) As a reminder, the in-memory layout of Swift types is not guaranteed.
NSData
methods; you will need to manually update your code if you want to use the new APIs.NSData(contentsOfMappedFile: x)
can be changed to Data(contentsOf: x, options: .mappedAlways)
NSData(data: x)
can be changed to x
NSDate
that have better representations in Swift 3:
(x as NSDate).earlierDate(y)
can be changed to x < y ? x : y
(x as NSDate).laterDate(y)
can be changed to x < y ? y : x
CALayer?
to CALayer
only unwraps optionals; did you mean to use ‘!’?
as! CALayer
and replace with !
.rawValue
call when passed into functions that accept the raw value instead of the new enum type.NSCache
-> Cache<Key,Value>
, NSMapTable
-> MapTable<Key,Value>
). After migrating to Swift 3 you may need to add appropriate generic parameters for them.@objc(objectiveC:name:)
attribute before the implementation of the optional requirement with the original Objective-C selector inside.NSWindowStyleMask(rawValue: 8345)
.NSMutable*
types that have value type equivalents (e.g. NSMutableData
-> Data
, NSMutableURLSession
-> URLSession
), but most SDK functions now expect the new value types.
as Data
), but this may cause additional copies.self
e.g.: let typeErasedSelf = self as! MyObjCType<AnyObject>
Pasteboard.readObjects(forClasses:options:
), the migrator may aggressively rename the first argument, e.g. NSURL.self
to URL.self
, this causes compiler errors; to solve the issue, users can discard the migrator’s changes.NSData(bytes:length:deallocator:)
.
(UnsafeMutablePointer<Void>, Int) -> Void
to (UnsafeMutablePointer<Int8>, Int) -> Void
#if os(iOS)
block.String(contentsOfURL:usedEncoding:)
to String(contentsOf:usedEncoding:)
which now accepts an inout String.Encoding
instead of an UnsafeMutablePointer
for the usedEncoding
argument.NSURL
to URL
, leading to compiler errors of unavailable members. To solve this issue, users may need to manually add cast, in the URL
example, something like x as NSURL
.DispatchQueue.global(attributes: DispatchQueue.GlobalAttributes.qosDefault)
to DispatchQueue.global(attributes: .qosDefault)
.The free function dispatch_once
is no longer available in Swift. In Swift, you can use lazily initialized globals or static properties and get the same thread-safety and called-once guarantees as dispatch_once
provided.
Example:
let myGlobal = { … global contains initialization in a call to a closure … }()
_ = myGlobal // using myGlobal will invoke the initialization code only the first time it is used.
DispatchSource
types. You should change dispatch_source_t
to one of these specific protocols, such as DispatchSourceTimer
, DispatchSourceProcess
, etc. as appropriate.DispatchAttributes
OptionSet
. If you previously used dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, 0))
, you should now use use the option set, as in DispatchQueue(label: name, attributes: [.serial, .qosDefault])
dispatch_get_specific
no longer takes an UnsafeMutablePointer<Void>
, and it does not add the required argument label.
UnsafeMutablePointer<Void>
keys with DispatchSpecificKey<T>
, and add the missing key:
label.ImplicitlyUnwrappedOptional
s.
?
after values of implicitly unwrapped optional type where !
would be more appropriate. This can allow a nil value to be silently propagated instead of deterministically trapping.
!
instead of ?
in these cases when you desire nil values to trap.if let
statements which no longer return optional.
if let
statement. If you need to keep a lexical scope, bring the binding inside a do
statement.var URL: NSURL
, it will be rewritten as var URL: Foundation.URL
struct MySequence: SequenceType
=> struct MySequence: Swift.Sequence
.
Swift.