Swift 【記一次Swift4.1→Swift4.2語法過渡】

 

背景

WARNING ITMS-90725: "SDK Version Issue. This app was built with the iOS 11.3 SDK.
Starting March 2019, all iOS apps submitted to the App Store must be built with the iOS 12.1 SDK or later, 
included in Xcode 10.1 or later."

2019年3月份起,就不可以用當下的Xcode 9.4.1打包上傳代碼了,升級Xcode 10迫在眉睫。上週五開會立下軍令狀,3日內完成Swift 4.1到Swift 4.2的語言升級。今天已是週一,好在如期完成,纔有時間在這閒扯幾句。

 

 

準備原料

 

更新你的Xcode

建議下載Xcode 10.1版本,不要直接覆蓋升級,因爲在對比代碼差異時會用到之前的。

 

準備最新的Cocoapods環境,你可能會用到以下幾個命令:

  • 安裝或升級RVM(Ruby Version Manager - Ruby版本管理器)
$ curl -L get.rvm.io | bash -s stable 
$ source ~/.bashrc  
$ source ~/.bash_profile 
$ rvm -v  

 

  • 用RVM升級Ruby
$ rvm list known
$ rvm install 2.4.1 
$ ruby -v 

 

  • 升級Cocoapods版本
$ sudo gem install -n /usr/local/bin cocoapods --pre
$ pod --version

 

更新本地Cocoapods庫:

可參考之前快速升級本地Cocoapods庫文件的辦法

https://wangzhongyao.blog.csdn.net/article/details/84169233

友情提示:執行上面命令的時候,不要考驗自己的網速夠不夠快,有5塊零錢去買個梯子好一些。

 

 

實現

 

升級Cocoapods引入的第三方庫文件

在升級語言時,如果有Pods庫未升級的阻隔,那大概率編譯後自己代碼中的error無法顯現。所以我們首先要做的是升級Pods庫。

如果引入的是OC寫的庫,就不必升級。

若是Swift寫的庫,則需要檢查是否適配了Swift 4.2語言。如下:

#if swift(>=4.2)

let curveUserInfoKey    = UIResponder.keyboardAnimationCurveUserInfoKey
let durationUserInfoKey = UIResponder.keyboardAnimationDurationUserInfoKey
let frameEndUserInfoKey = UIResponder.keyboardFrameEndUserInfoKey

#else

let curveUserInfoKey    = UIKeyboardAnimationCurveUserInfoKey
let durationUserInfoKey = UIKeyboardAnimationDurationUserInfoKey
let frameEndUserInfoKey = UIKeyboardFrameEndUserInfoKey

#endif

當然,在檢查引入的第三方庫時,可能引入該庫的當前版本並沒有適配。我們可升級庫版本到最新,然後檢查是否可用。

在所有Pods中第三方庫均檢查完畢後,我們可以新建一個基於Swift 4.2的空白項目,然後將修改完畢後的Pods導入,運行如果無誤,那麼Pods的升級完畢。

 

升級你自己的代碼

在Pods升級完畢後,你自己代碼中的bug會一股腦兒的出來。如下圖:

error 999+?這其實只是冰山一角。具體怎麼改?95%的bug可以通過提示點一點解決。4%呢需要下點兒功夫,比如說一些方法變更、枚舉變更後無法通過提示一鍵生成。文章後面附更改方法。

大概改了1天,剛過了情人節沒幾天,嗯,這個數字還不錯。

差不多第二天了,嗯,兩位數紀念一下。

當然問題大同小異,直到最後一個error,作難了。如下圖:

這種error並沒有定位到哪一行,這個error在編譯器提示的數量爲1,也就是這麼多歎號,實際上可能針對的只是一處錯誤。Xcode 10畢竟初來乍到,並不完善。

經資料查證,Segmentation fault: 11 ,代表指令錯誤,程序在編譯時無法將代碼翻譯成指令集。仔細檢測對應位置的代碼是否正確。出現情況較多的是 nil 類型沒有做解包操作(注意 ?? 雙可選類型),數組類型不一致等情況。

一行一行去檢查自己項目,果然在兩個文件下發現瞭如下提示:

可以翻譯爲“編譯器發生內部錯誤。源編輯器功能有限。試圖恢復...”。當然如果點了右邊的小箭頭,那麼就跳轉至Xcode官方提bug工單的平臺了。當然能不能收到回覆就另說了,所以建議不點。

如果存在編譯異常的文件並且和其他文件關聯較少,那建議註釋掉。切斷與其他文件的關聯後,再次編譯,此方法可行。但如果編譯異常的文件爲基類,與其他的文件耦合度較高,那不建議這麼做,只能一行一行的去檢查判空、可選類型等操作。

還好我運氣不錯,快速定位到了問題,如下:

這存在於高德地圖的某代理方法中,該值爲OC中的NSString類型,延伸到Swift中出現了String!?類型。如果你的項目也出現了該情況,可以這樣解決:

let subtitle = (annotation.subtitle ?? "") ?? ""

解包後可放心使用。

最後整理一下代碼,運行...

 

附:Swift 4.1 ---> Swift 4.2 語法變更(可直接粘貼替換,十分方便)

### UIImagePickerControllerDelegate
在 Swift 4.2 中 `UIImagePickerControllerReferenceURL`  `UIImagePickerControllerOriginalImage` 等由常量變爲了 Struct:
```
public struct InfoKey : Hashable, Equatable, RawRepresentable {
    public init(rawValue: String)
}
```
所以以下方法也需要跟着修改,如果不改是不會執行該代理方法的:
```
// Swift 4.1
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
```
改爲:
```
// Swift 4.2
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any])
```
### AppDelegate
同理,還有 AppDelegate 中的方法:
#### 1
```
// Swift 4.1
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
```
修改爲:
```
// Swift 4.2
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool
```
#### 2
```
// Swift 4.1
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool
```
修改爲:
```
// Swift 4.2
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool
```
#### 3
```
// Swift 4.1
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool
```
修改爲:
```
// Swift 4.2
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool
```
## 其他變更
以下是我在升級過程中遇到的變更情況,大致整理爲「通知相關,常量變更,類型變更,方法變更」四類,共大家參考:
## 通知相關
#### Notification.Name.UIApplicationWillResignActive
```
// Swift 4.1
Notification.Name.UIApplicationWillResignActive
```
```
// Swift 4.2
UIApplication.willResignActiveNotification
```
#### Notification.Name.UITextViewTextDidChange
```
// Swift 4.1
Notification.Name.UITextFieldTextDidChange
```
```
// Swift 4.2
UITextField.textDidChangeNotification
```
#### Notification.Name.UIKeyboardWillShow
```
// Swift 4.1
Notification.Name.UIKeyboardWillShow
```
```
// Swift 4.2
UIResponder.keyboardWillShowNotification
```
#### Notification.Name.UIKeyboardWillHide
```
// Swift 4.1
Notification.Name.UIKeyboardWillHide
```
```
// Swift 4.2
UIResponder.keyboardWillHideNotification
```
## 常量變更
##### UILayoutFittingExpandedSize
```
UIKIT_EXTERN const CGSize UILayoutFittingCompressedSize NS_AVAILABLE_IOS(6_0);
UIKIT_EXTERN const CGSize UILayoutFittingExpandedSize NS_AVAILABLE_IOS(6_0);
```
UILayoutFittingExpandedSize 由常量變爲了UIView 的 class 屬性
```
// Swift 4.1
UILayoutFittingExpandedSize
```
```
// Swift 4.2
UIView.layoutFittingExpandedSize
```
```
// Swift 4.1
UILayoutFittingCompressedSize
```
```
// Swift 4.2
UIView.layoutFittingCompressedSize
```
##### AVAudioSessionRouteChangeReason
```
// Swift 4.1
AVAudioSessionRouteChangeReason
```
```
// Swift 4.2
AVAudioSession.RouteChangeReason
```
#### UIKeyboardFrameEndUserInfoKey
```
// Swift 4.1
UIKeyboardFrameEndUserInfoKey
```
```
// Swift 4.2
UIResponder.keyboardFrameEndUserInfoKey
```
#### UIKeyboardAnimationDurationUserInfoKey
```
// Swift 4.1
UIKeyboardAnimationDurationUserInfoKey
```
```
// Swift 4.2
UIResponder.keyboardAnimationDurationUserInfoKey
```
#### UIKeyboardAnimationCurveUserInfoKey
```
// Swift 4.1
UIKeyboardAnimationCurveUserInfoKey
```
```
// Swift 4.2
UIResponder.keyboardAnimationCurveUserInfoKey
```
#### kCAFillModeForwards
```
// Swift 4.1
kCAFillModeForwards
```
```
// Swift 4.2
CAMediaTimingFillMode.forwards
```
#### kCAMediaTimingFunctionEaseInEaseOut
```
// Swift 4.1
kCAMediaTimingFunctionEaseInEaseOut
```
```
// Swift 4.2
CAMediaTimingFunctionName.easeInEaseOut
```
#### kCALineJoinMiter
```
// Swift 4.1
kCALineJoinMiter
```
```
// Swift 4.2
CAShapeLayerLineJoin.miter
```
### 幾種從 String 常量變爲 Struct 類型
#### UIImagePickerControllerReferenceURL
```
// Swift 4.1
UIImagePickerControllerReferenceURL
```
```
// Swift 4.2
UIImagePickerController.InfoKey.referenceURL
```
#### UIImagePickerControllerOriginalImage
```
// Swift 4.1
UIImagePickerControllerOriginalImage
```
```
// Swift 4.2
UIImagePickerController.InfoKey.originalImage
```
#### UIImagePickerControllerCropRect
```
// Swift 4.1
UIImagePickerControllerCropRect
```
```
// Swift 4.2
UIImagePickerController.InfoKey.cropRect
```
#### UIImagePickerControllerMediaType
```
// Swift 4.1
UIImagePickerControllerMediaType
```
```
// Swift 4.2
UIImagePickerController.InfoKey.mediaType
```
## 類型變更
##### UITableViewCellStyle
```
// Swift 4.1
UITableViewCellStyle
```
```
// Swift 4.2
UITableViewCell.CellStyle
```
##### UIWindowLevelAlert
```
// Swift 4.1
UIWindowLevelAlert
```
```
// Swift 4.2
UIWindow.Level.alert
```
##### UIViewAnimationCurve
```
// Swift 4.1
UIViewAnimationCurve
```
```
// Swift 4.2
UIView.AnimationCurve
```
##### UIAlertActionStyle
```
// Swift 4.1
UIAlertActionStyle
```
```
// Swift 4.2
UIAlertAction.Style
```
##### UIViewContentMode
```
// Swift 4.1
UIViewContentMode
```
```
// Swift 4.2
UIView.ContentMode
```
##### RunLoopMode
```
// Swift 4.1
RunLoopMode
```
```
// Swift 4.2
RunLoop.Mode
```
##### NSAttributedStringKey
```
// Swift 4.1
NSAttributedStringKey
```
```
// Swift 4.2
NSAttributedString.Key
```
##### UIViewAnimationOptions
```
// Swift 4.1
UIViewAnimationOptions
```
```
// Swift 4.2
UIView.AnimationOptions
```
##### UITableViewAutomaticDimension
```
// Swift 4.1
UITableViewAutomaticDimension
```
```
// Swift 4.2
UITableView.automaticDimension
```
##### UIApplicationLaunchOptionsKey
```
// Swift 4.1
UIApplicationLaunchOptionsKey
```
```
// Swift 4.2
UIApplication.LaunchOptionsKey
```
##### UICollectionViewScrollPosition
```
// Swift 4.1
UICollectionViewScrollPosition
```
```
// Swift 4.2
UICollectionView.ScrollPosition
```
##### UIApplicationOpenURLOptionsKey
```
// Swift 4.1
UIApplicationOpenURLOptionsKey
```
```
// Swift 4.2
UIApplication.OpenURLOptionsKey
```
##### UIViewAutoresizing
```
// Swift 4.1
UIViewAutoresizing
```
```
// Swift 4.2
UIView.AutoresizingMask
```
##### AVPlayerStatus
```
// Swift 4.1
AVPlayerStatus
```
```
// Swift 4.2
AVPlayer.Status
```
##### NSUnderlineStyle
NSUnderlineStyle寫法更簡潔了
```
// Swift 4.1
NSUnderlineStyle.styleSingle
```
```
// Swift 4.2
NSUnderlineStyle.single
```
##### UIButtonType
```
// Swift 4.1
UIButtonType
```
```
// Swift 4.2
UIButton.ButtonType
```
##### UIControlState
```
// Swift 4.1
UIControlState
```
```
// Swift 4.2
UIControl.State
```
##### UIControlEvents
```
// Swift 4.1
UIControlEvents
```
```
// Swift 4.2
UIControl.Event
```
##### UIAlertControllerStyle
```
// Swift 4.1
UIAlertControllerStyle
```
```
// Swift 4.2
UIAlertController.Style
```
##### UICollectionElementKindSectionHeader
```
// Swift 4.1
UICollectionElementKindSectionHeader
```
```
// Swift 4.2
UICollectionView.elementKindSectionHeader
```
```
// Swift 4.1
UICollectionElementKindSectionFooter
```
```
// Swift 4.2
UICollectionView.elementKindSectionFooter
```
##### UIBarButtonItemStyle
```
// Swift 4.1
UIBarButtonItemStyle
```
```
// Swift 4.2
UIBarButtonItem.Style
```
##### NSAttributedStringKey
```
// Swift 4.1
NSAttributedStringKey
```
```
// Swift 4.2
NSAttributedString.Key
```
##### UIApplicationOpenSettingsURLString
```
// Swift 4.1
UIApplicationOpenSettingsURLString
```
```
// Swift 4.2
UIApplication.openSettingsURLString
```
## 方法變更
#### MKCoordinateRegionMake(CLLocationCoordinate2D centerCoordinate, MKCoordinateSpan span)
```
// Swift 4.1
MKCoordinateRegionMake(a, b)
```
```
// Swift 4.2
MKCoordinateRegion(center: a, span: b)
```
#### MKCoordinateSpanMake(CLLocationDegrees latitudeDelta, CLLocationDegrees longitudeDelta)
```
// Swift 4.1
MKCoordinateSpanMake(0.1, 0.1)
```
```
// Swift 4.2
MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
```
#### UIAccessibilityIsVoiceOverRunning()
```
// Swift 4.1
UIAccessibilityIsVoiceOverRunning()
```
```
// Swift 4.2
UIAccessibility.isVoiceOverRunning
```
#### UIEdgeInsetsMake
```
// Swift 4.1
UIEdgeInsetsMake(10, 0, 40, 0)
```
```
// Swift 4.2
UIEdgeInsets(top: 10, left: 0, bottom: 40, right: 0)
```
#### UIEdgeInsetsInsetRect(rect, insets)
```
// Swift 4.1
UIEdgeInsetsInsetRect(rect, insets)
```
```
// Swift 4.2
rect.inset(by: insets)
```
#### NSStringFromCGPoint(CGPoint point);
```
// Swift 4.1
NSStringFromCGPoint(x)
```
```
// Swift 4.2
NSCoder.string(for: x)
```
#### didMove(toParentViewController:)
```
// Swift 4.1
viewController.didMove(toParentViewController: self)
```
```
// Swift 4.2
viewController.didMove(toParent: self)
```
#### addChildViewController()
```
// Swift 4.1
addChildViewController(viewController)
```
```
// Swift 4.2
addChild(viewController)
```
#### removeFromParentViewController
```
// Swift 4.1
viewController.removeFromParentViewController()
```
```
// Swift 4.2
viewController.removeFromParent()
```
##### var childViewControllers:[UIViewController]
```
// Swift 4.1
let array = viewController.childViewControllers
```
```
// Swift 4.2
let array = viewController.children
```
#### bringSubview(toFront:)
```
// Swift 4.1
bringSubview(toFront: view)
```
```
// Swift 4.2
bringSubviewToFront(view)
```
#### sendSubview(toBack: headerView)
```
// Swift 4.1
sendSubview(toBack: headerView)
```
```
// Swift 4.2
sendSubviewToBack(headerView)
```
##### UIImageJPEGRepresentation(,)
```
// Swift 4.1
let data = UIImageJPEGRepresentation(image, 0.6)
```
```
// Swift 4.2
let data = image.jpegData(compressionQuality: 0.6)
```
##### UIDatePickerMode
```
// Swift 4.1
UIDatePickerMode
```
```
// Swift 4.2
UIDatePicker.Mode
```
##### AVAudioSession.RouteChangeReason
```
// Swift 4.1
UIScrollViewDecelerationRateFast
```
```
// Swift 4.2
UIScrollView.DecelerationRate.fast
```
##### UITableViewCellEditingStyle
```
// Swift 4.1
UITableViewCellEditingStyle
```
```
// Swift 4.2
UITableViewCell.EditingStyle
```
##### AVAudioSessionInterruptionType
```
typedef NS_ENUM(NSUInteger, AVAudioSessionInterruptionType)
{
AVAudioSessionInterruptionTypeBegan = 1,  /* the system has interrupted your audio session */
AVAudioSessionInterruptionTypeEnded = 0,  /* the interruption has ended */
};
```
```
// Swift 4.1
AVAudioSessionInterruptionType
```
```
// Swift 4.2
AVAudioSession.InterruptionType
```
##### CMTimeMake
```
// Swift 4.1
CMTimeMake(0, 1)
```
```
// Swift 4.2
CMTimeMake(value: 0, timescale: 1)
```
#### AVAudioSession setCategory
AVAudioSession的 setCategory不能像之前版本不填寫 mode了,新版寫法:
```
try? AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.ambient, mode: .default)
```

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章