iOS——Swift 4.0 适配实战总结(Xcode9)

iOS的小伙伴有没有觉得今年特别与众不同,是因为iPhone X出来了吗?是的,但是不仅仅是因为iPhone X的面世。 还有 Xcode 9无线调试,Swift 4升级适配,iPhone X适配,前面介绍了iPhone X适配总结,这里整理一下Swift 4.0升级与适配处理…..

前言

ios开发中,适配越来越多了:
  • Xcode适配
  • Swift升级适配
  • iPhone适配

其中Xcode没有太多东西可说,最重要的是iPhone适配,尤其是最新的iPhone X的适配。

iPhone X的适配之前有整理过一篇文章根据实际进行总结iPhone X适配实战总结

这里主要介绍一下最新版Swift 4适配,并简单的说一下关于Xcode9特性与适配的问题

关于Swift新特性可以参考这里:http://www.jianshu.com/p/f35514ae9c1a

Xcode 9 中同时集成了 Swift 3.2 和 Swift 4。

  1. Swift 3.2 完全兼容 Swift 3.1,并会在过时的语法或函数上报告警告。
  2. Swift 3.2 具有 Swift 4 的一些写法,但是性能不如 Swift 4。
  3. Swift 3.2 和 Swift 4 可以混合编译,可以指定一部分模块用 Swift 3.2 编译,一部分用 Swift 4 编译。
  4. 迁移到 Swift 4 后能获得 Swift 4 所有的新特性,并且性能比 Swift 3.2 好。

当 Xcode 正式版发布后,现有的 Swift 代码可以直接升级到 Swift 3.2 而不用做任何改动,也可以后续再迁移到 Swift 4。
或者直接迁移到 Swift 4 也可以,Swift 4 相比 Swift 3 的 API 变化还是不大的,很多第三方库都可以直接用 Swift 4 编译。
Swift 1 到 2 和 Swift 2 到 3 的迁移的痛苦在 3 到 4 的迁移上已经大大改善了。

适配

关于Swift 4适配中OC与Swift混编的坑比较多

查看当前版本

曹理鹏(iCocos)-梦工厂

当前环境

  • Mac OS 10.12.6
  • XCode 9.1
  • 当前Swift版本 3.2

一键升级

这一特性非常6,印象中是swift2的时候出来的,具体时间也忘了。

曹理鹏(iCocos)-梦工厂

  • 然后勾选需要转换的 target (pod 引用不用勾选),Next
  • 然后选择转换选项,Next
    这两个选项是关于 swift 的 @objc 推断特性的,如果使用了 swift4.0 显式的 @objc 属性,能减少整体代码的大小。此时我们选 Minimize Inference(recommend)
    曹理鹏(iCocos)-梦工厂
    • Minimize Inference(recommend)
      • 根据静态推断,仅在需要的地方添加@objc属性。使用此选项后,需要按照Completing a Swift 4 minimize inference migration来完成转换。
    • Match Swift 3 Behavior
      • 在编译器隐式推断的任何地方向代码添加一个@objc属性。这个选项不会改变你的二进制文件的大小,因为被Swift 3隐式推断在所有的地方都添加了显式的@objc属性。

修改错误+细节

完成上面之后,不会发现当前版本确实编程的Swift 4,但是好像跑步起来,到处报错。

  • 对,毕竟是工具,不可能那么人性化,有些地方还是需要人工进行专门的适配

问题一:编译不通过

如果项目中之前有class和extension,有些也给OC调用。在OC的代码中,我们通过#import “ModuleName-Swift.h”导入了Swift文件。如果是Swift3.2,一切都能正常工作,但是在Swift4.0上,编译通不过了。

如果你看了Swift 4特性的话应该知道

swift4.0 最大的特性之一就是 @objc 修饰符的变化了,它主要处理 OC 和 swift 混编时一些方法的调用以及属性获取问题,swift4.0 将在 swift3.x 中一些隐式类型推断的特性去除以后,需要我们来手动管理 @objc 修饰符。

具体解决方案:
  • 一:在OC中调用一个Swift4.0类的方法(包括实例方法、static方法、class方法),你需要:
    • 在该Swift4.0类前加上修饰符@objc
    • 该Swift4.0类必须继承NSObject(否则,无法在前面加上修饰符@objc。当然,这里指的是普通类,@objc也是可以修饰UI开头的一系列UIKit框架下的UI类,只是修饰了这些类,不会产生什么影响)
    • 在需要调用的方法前加上修饰符@objc
  • 二:在OC中调用一个Swift4.0扩展的属性(包括实例属性、static属性、class属性)、方法(包括实例方法、static方法、class法),你有如下两种选择方式:
    • 在该Swift4.0扩展前加上修饰符@objc(这样的话,该扩展下的所有的属性、方法,都可被OC调用)。
    • 在需要的属性、方法前直接加上@objc修饰,也可达到目的。

注意一点:swift3 使用 #selector 指定的方法,只有当方法权限为 private 时需要加 @objc 修饰符,swift4.0 都要加 @objc 修饰符
swift4.0 不再允许重载 extension 中的方法(包括instance、static、class方法)

问题二:运行时找不到属性

如果你有一个Swift类继承自UIViewController,OC中调用或者操作这个类[viewController valueForKey:@”userName”]这一KVC方法去获取这个自定义UIViewController中的iconURL这一属性的属性值。

这种方式,编译时是无法检查出问题的。但是在运行时,问题就来了,找不到这个属性。因为这个属性没有暴露给OC来进行调用。

解决方案:
  • 仅需要在自定义的UIViewController类中给需要暴露给OC调用的属性前加上@objc修饰符便可。如此一来,在OC代码中就能访问到这个属性。
    • (注意:这里可不像上面提到的extension一样,在这个已定义的UIViewController类前面加上@objc修饰符没有任何意义)。

编译警告

  • swift 中编译的警告
    • “#selector” 参数指定的实例方法必须使用 @objc 修饰,因为swift4中弃用了 @objc属性推断。
  • Objective-C 编译时警告
    • 在 OC 中调用的 swift 方法,在 swift 中需要追加 @objc 修饰,swift4 废弃了该类型推断。
关于编译时也是直接增加@objc即可

运行时警告

运行时警告会打印在控制台:

***Swift runtime: 
ClassName.swift:lineInFile:columnInLine: 
entrypoint -[ClassName methodName] generated by implicit @objc inference is deprecated and will be removed in Swift 4; 
add explicit @objc to the declaration to emit the Objective-C entrypoint in Swift 4 and suppress this message

同样:想要修复运行时警告,需要添加 @objc 修饰符到对应的方法或者符号。

  • 运行时警告的常见原因:
    • 在 OC 中使用 SEL
    • 在 swift 中使用了 perform methods
    • 在 OC 中使用了 performSelector methods
    • 使用了 @IBOutlet 或者 @IBAction

NSAttributedStringKey

swift3.x
public init(string str: String, attributes attrs: [AnyHashable : Any]? = nil)

swift4.0
public init(string str: String, attributes attrs: [NSAttributedStringKey : Any]? = nil)

String

废弃characters
swift 3
var count = string.characters.count

error
'characters' is deprecated: Please use String or Substring directly

swift 4
count = string.count
废弃addingPercentEscapes
swift 3
var url = @"http://www.example.com?username=姓名"
url = url.addingPercentEscapes(using: String.Encoding.utf8)!

error
'addingPercentEscapes(using:)' is unavailable: Use addingPercentEncoding(withAllowedCharacters:) instead, which always uses the recommended UTF-8 encoding, and which encodes for a specific URL component or subcomponent since each URL component or subcomponent has different rules for what characters are valid.

swift 4
uri = uri.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!
废弃substring(to:)
swift 3
let index = tagText.index(tagText.startIndex, offsetBy: MPMultipleStyleListItemTagMaxLength)

// 警告:'substring(to:)' is deprecated: Please use String slicing subscript with a 'partial range upto' operator.
let b = tagText.substring(to: index)

Swift 4
let a = tagText.prefix(upTo: index) //a 的类型是 Substring,不是 String

pod 引用

添加以下内容到 Podfile。

post_install do |installer|
    installer.pods_project.targets.each do |target|
        if ['WTCarouselFlowLayout', 'XSLRevenue', 'OHHTTPStubs/Swift'].include? target.name
            target.build_configurations.each do |config|
                config.build_settings['SWIFT_VERSION'] = '3.2'
            end
        end
    end
end

系统方法

UITableViewDelegate 协议方法名变更,没有错误提示:

// swift3.x
func tableView(_ tableView: UITableView, heightForRowAtIndexPath indexPath: IndexPath) -> CGFloat 

// swift4.0
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat 

Xcode 9

关于Xcode 9适配其实并没有太多可说的地方,具体可参考苹果官方Session,但是我相信有一点是很多开发都非常喜欢的特性:无线调试

好处:
  • 不用经常买线,不用担心接口或者插口坏了
  • 不用担心忘记带线,无法调试
  • 不用每次都插着才能调试(嘿,测试的MM,我给你装个最新的包,你接好了)
要求
  1. 必须是Xcode9-beta以上
  2. iPhone系统需iOS11以上
操作
  1. 在Xcode9-beta菜单的Window选项中选择Devices and Simulators
  2. 通过连接线让你的Mac识别到你的iPhone
  3. 在Devices and Simulators面板的左侧Connected菜单中选择连接的设备
  4. 在顶部的Devices和Simulators选项中选择Devices(这里其实默认就是选择了Devices),
  5. 勾选Connect via network选项。
关于Xcode无线调试可参考下面地址

WWDC17惊喜——Xcode9无线调试
https://icocos.github.io/2017/06/13/WWDC17%E6%83%8A%E5%96%9C%E2%80%94%E2%80%94Xcode9%E6%97%A0%E7%BA%BF%E8%B0%83%E8%AF%95/

总结

Swift3.2到Swift4.0的改变(只是我项目中遇到的):

  1. Swift4.0中对于扩展的属性(包括实例属性、static属性、class属性),都只能使用get方法,不可使用set方法

  2. Swift4.0中不再允许复写扩展中的方法(包括实例方法、static方法、class方法)

  3. 编译期与运行时警告处理,添加 @objc 修饰符到对应的方法或者符号。

  4. swift3使用#selector指定的方法,只有当方法权限为private时需要加@objc修饰符,现在全都要加@objc修饰符

  5. 字体方面的一些重命名

    • NSFontAttributeName重命名为NSAttributedStringKey.font、
    • NSForegroundColorAttributeName重命名为NSAttributedStringKey.foregroundColor、
    • NSStrikethroughStyleAttributeName重命名为NSAttributedStringKey.strikethroughStyle、
    • size(withAttributes:)方法重命名为size(withAttributes:))

官方参考资料

  • 《Swift Language Programming (Swift 4.0)》
  • WWDC 2017 Session 402 《What’s New in Swift》
  • WWDC 2017 Session 212 《What’s New in Foundation》
  • WWDC 2017 Session 102 《Platforms State of the Union》
坚持原创技术分享,您的支持将鼓励我继续创作!