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)
```

 

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