寫在前面
- Swift 3.0 正式版釋出了差不多快一個月了,斷斷續續的把手上和 Swift 相關的遷移到了Swift 3.0。所以寫點小總結。
背景
程式碼量(4萬行)
- 首先,我是今年年初才開始入手 Swift 的。加上 Swift 的 ABI 和 API 一直不穩定,所以沒有在專案中大範圍的使用,所以這次遷移的程式碼量不多,大概在4萬行左右。
遷移時間(一天左右)
- 遷移時間上的話,大概是花了1天左右。兩個混編專案,一個 Swift 為主的專案。期中 Swift 為主的專案 花了大概大半天時間,兩個混編程式碼量差不多,但是一個花了小半天,還有一個差不多隻花了半個小時(原因先留個懸念~)。
準備
在開發最初開發選擇 Swift 的時候的很多決策也讓我這次少了很多工作量。
介面用 xib 而不用純程式碼
- 陰差陽錯的,和 Swift 相關的大部分介面都是用xib 畫的。而這個 xib 在這次遷移中得到了很大的優勢,xib 和 SB 的程式碼不適配 Swift 3。想當初要是使用程式碼寫的 UI 的話,這次遷移改動估計會多很多吧。
關於第三方庫的選擇:
- 對於一個專案來說,三方庫似乎成了一道必選菜,但是如何去選擇這道菜呢?
- 對於三方庫,當初的選擇是,能用 OC 就儘量用 OC。 畢竟可以OC 可以無縫銜接到 Swift,而且還相對穩定。
- 在選擇 Swift 相關的三方庫時,我儘量值選擇使用者比較多的庫,例如Alamofire、Snap、Kingfisher、Fabric 等,因為使用者比較多,開發者會更願意去維護,而不至於跳票。所以不會存在現在許多小夥伴面臨的問題,想遷移,但是有些庫沒有更新。至少對於我來說,當我想遷移的時候,所有和 Swift 相關的三方庫都已經遷移到了 3.0 了。
得益於上面兩點,在遷移過程中少了不少工作量。?
知識儲備升級
- 先了解了一下Swift 2 到 Swift 3 的變動,及變動的原因。(看完心中一萬頭草泥馬飛過,但是其實是越來越好了)
- 然後把語法文件快速的重溫了一遍。
遷移中的問題
Any && AnyObject
- 我想在做遷移和做完遷移的同學改的最多的一個就是
as AnyObjct?
吧? - 至少對於我來說是的。
- 和這個相關的基本是集合型別。在 Swift 2 中我們一個用 [AnyObject] 來存放任何變數,甚至於存放struct型別的
String
、Array
等。但是按道理 Swift 的 AnyObject 指的是類,而 Any 才是包括struct
、class
、func
等所有型別。但是為何 Struct 可以放入 [AnyObject] 呢?在 Swift 2 的時候會針對String、Int 等 Struct 進行一個 Implicit Bridging Conversions。而到了 Swift 3 則進行了一個**Fully eliminate implicit bridging conversions from Swift**改動。 - 當然在我的專案中[AnyObject]其實是小事,最麻煩的就是 [String:AnyObject]。因為當初寫專案的時候,還是處於 OC To Swift 的階段所以對於 Dictionary ,基本採用了 [String:AnyObject], 所以在修改的時候,在很多地方為了這個修改。
- 起初,我是照著 Xcode 的提示,在 Dictionary 後面的 value 後面加了一個
as AnyObjct?
- 後來漸漸的發現我做了一件很傻比的事情,其實我只要把 [String:AnyObject] 改為 [String:Any] 就可以了。?
- 起初,我是照著 Xcode 的提示,在 Dictionary 後面的 value 後面加了一個
- 這也就是為什麼在第一混編的專案中我花了那麼多時間去修改程式碼了!得益於混編的第二個專案學習了 Yep 的思路,是把
[String:AnyObject]
命名為一個叫做JSONDictionary
的型別。所以在 Any && AnyObect 這個事情上,就花了一點點時間。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// Swift 2 var json = [String:AnyObect]() json["key1"] = 1 json["key2"] = "2" // to Swift 3 Step 1 var json = [String:AnyObect]() json["key1"] = 1 as AnyObject? json["key2"] = "2" as AnyObject? // to Swift 3 Step 2 var json = [String:Any]() json["key1"] = 1 json["key2"] = "2" // Swift 2 public typealias JSONDictionary = [String: AnyObject] // To Swift 3 Step 2 public typealias JSONDictionary = [String: Any] |
Alamofire 等三方庫支援 iOS8
- 雖然說我使用的三方庫都在第一時間將庫升級到了 Swift 3 ,但是期中 Alamofire 和 Snap 兩個庫最低適配只支援到了 iOS 9,為了避免和產品撕逼,不得不想辦法解決這個適配問題。下面以 Alamofire 為例
- 其實三方庫麼,不一定只用 Cocoapods 的。所以打算下載程式碼然後直接擼原始碼。
- 先Alamofire的 Xcode 修改為最低適配 8.0,然後編譯查詢不通過的函式,並刪除。(其實這些函式都是 iOS 9 新加的函式,所以刪除不影響什麼。)
- 大概花了 半個小時左右就可以刪完了,然後直接拖到專案中就可以了~
- Snap 其實只要拖進去就好了,暫時不需要修改什麼。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// 其實都是 !os(watchOS) 這個巨集下面的 #if !os(watchOS) @discardableResult public func stream(withHostName hostName: String, port: Int) -> StreamRequest { return SessionManager.default.stream(withHostName: hostName, port: port) } @discardableResult public func stream(with netService: NetService) -> StreamRequest { return SessionManager.default.stream(with: netService) } #endif |
@escaping
- 這個是我在適配中最蛋疼的坑
- 首先在看swift-evolution只是瞭解到@escaping 必須顯示宣告。但是不知道@escaping的閉包,在函式體內無法再修改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
let pedonmeter:CMPedometer = CMPedometer() func getPedometerDataFromDate(_ datet:Date?, withHandler handler: @escaping (CMPedometerData?, Error?) -> ()){ // 編譯錯誤 pedonmeter.queryPedometerDataFromDate(startTime, toDate:endTime, withHandler: { (pedometerData:CMPedometerData?, error:NSError?) -> Void in guard let pedometerData = pedometerData else { return } handler(pedometerData, error) // 做一些事情 }) // 最後逼不得已只能不修改了,函式外面就做一些事情了 pedonmeter.queryPedometerData(from: startTime, to: endTime, withHandler: handler as! CMPedometerHandler) } |
Result of call to ‘funtion’ is unused
- 這其實不是一個 編譯錯誤,但是這個警告最開始讓我有點懵逼.返回值不用難道要我都修改一下?
- 最開始其實我是這麼修改的
let _ = funtion()
,但是後面在看SE-0047的時候發現@discardableResult
也是可以達到這個效果的。
Date && NSDate
- 因為有個專案中使用的 DateTools 這個工具。它有一個 NSDate + Tools 的分類。
- 但是在寫 Swift 3 的過程中我發現如果變數是 Date 型別的無法使用NSDate + Tools 這個型別,必須顯示宣告 date as NSDate 這樣才能呼叫分類的一些個方法。
- 這個讓使用 OC 的庫的時候會感覺十分不舒服,畢竟很多 NS 的字首去掉了。所有都顯示宣告太不友好了。
CAAnimationDelegate
- 這個其實好像是 Xcode 8 的修改。因為之前CAAnimationDelegate 是一個分類。大概宣告如下:
1 2 3 4 5 6 7 |
@interface NSObject (CAAnimationDelegate) - (void)animationDidStart:(CAAnimation *)anim; - - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag; @end |
- 之前是在 vc 中只要重寫一下
animationDidStart
函式就可以了。但是新的不行,起初以為是 Swift 3 的變化,但是其實是 Xcode 8 中的修改。將 CAAnimationDelegate 變成了一個協議。我感覺這個修改是為了適配 Swift 3 ?變化如下:
1 2 3 4 5 6 7 8 |
@protocol CAAnimationDelegate <NSObject> @optional - (void)animationDidStart:(CAAnimation *)anim; - - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag; @end |
因為寬度時間比較長,其他的暫時想不到了。未完待續吧…
其他
- 還有許多微妙的變化讓你似乎看不懂這個語言了,所以建議在適配之前看一下下面的文章。
- Swift 3 新特性一覽
- [Swift 3.0 – Released on September 13, 2016]https://github.com/apple/swift-evolution/blob/master/releases/swift-3_0.md
- 還有@卓同學 的 Swift 3 必看系列
- 還有幾個不錯的總結
- Swift 3 by 顧 鵬
- 適配 Swift 3 的一點小經驗和坑 by 圖拉鼎
總結
- 總的說來這次遷移沒有想象中的那麼痛苦,雖然提案的改動很大,但是得益於 Xcode 8 的遷移工具,這次遷移花費時間不多,當然也有可能和我的程式碼量有關係~
- 在遷移完之後,再看程式碼,會發現 Swift 更加的優雅了,至少相比於 2 來說好了很多,至於好在哪裡?你自己寫寫不就知道了咯。
- 最後,終於可以把 Xocde 7 解除安裝,再也不用擔心兩個一起開無腦閃退了!!!
- 最後對於明年的 Swift 4 只想說 快來吧~分分鐘把你解決!
- 其實適配之路才剛剛開始,因為 Xcode 8 自動轉的程式碼並沒有很好的 Swift 3 化。目前只是說在 Swift 3 可以編譯通過了而已~
更多
工作之餘,寫了點筆記,如果需要可以在我的 GitHub 看。