适配Xcode9.0-beta与Swift4.0

适配Xcode9.0-beta与Swift4.0

原文地址:http://blog.jiar.vip/2017/06/09/%E9%80%82%E9%85%8DXcode9-0-beta%E4%B8%8ESwift4.0/
简书地址:http://www.jianshu.com/p/1f702d59e54b
您能够经过保留原文地址或者简书地址的方式进行转载。html

这几天苹果在开WWDC2017大会,期间放出了Xcode9.0-beta以及Swift4。为了响应苹果爸爸的号召,我果断下载了Xcode9.0-beta,并在项目中拉出了新的分支,准备搞事。ios

Xcode9.0-beta

如何适配

Xcode9.0-beta内置的Swift版本不止一个,它同时支持Swift4.0Swift3.2。而咱们正在用的Xcode8,最高只支持Swift3.1。基于这个事实,我先拉一个Xcode9.0-beta-Swift3.2的分支,待适配好Swift3.2后,再起分支Xcode9.0-beta-Swift4.0去支持Swift4.0git

适配Swift3.2

首先,对于Swift3.2,个人理解是:既然版本命名为3.2,那么应该只是基于3.1版本上的微调(我去查Swift,查到更多的是关于Swift4.0方面的信息)。适配Swift3.2的过程当中,个人项目代码不须要任何改动,惟一出问题的是一个第三方库:Eureka,报错的缘由是Collection协议的subscript返回值从Array变成了ArraySlice,关于这个问题,已有人在Eureka的issues中提出(#1082)。随后有人commit修复了这个问题,并开出新分支来适配Swift3.2github

Eureka-commit

最后,我在Podfile中修改pod 'Eureka'pod 'Eureka', :git => 'https://github.com/xmartlabs/Eureka.git', :branch => 'swift3.2',完成了适配Swift3.2swift

因而可知,适配Swift3.2几乎是没有什么压力的,我也就看到Collection协议的subscript返回值变更这个状况。api

适配Swift4.0

并非全部库都能作到及时支持Swift4.0,更况且是在如今连Xcode9也仍是beta的状态,因此咱们仅能作到将本身的业务代码(主工程代码)部分升级到Swift4.0,而后同时保留各类pod库在Swift3.2版本。没办法,谁叫Swift4.0也还没法作到ABI兼容呢(希望能在Swift5以前实现吧)。至于我说的同时使用两个版本的Swift,这是没问题的,Xcode9支持在项目中同时使用Swift3.2Swift4.0xcode

具体要怎么作呢?(修改Swift版本)

第一步,以下图指定主工程的Swift版本为4.0
Project-Build-Settings-Swift-Language-Version
第二步,以下所示,在Podfile文件的最下方加入以下代码,指定pod库的Swift版本为3.2(这样会使得全部的第三方pod库的Swift版本都为3.2)微信

post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['SWIFT_VERSION'] = '3.2'
    end 
  end
end

作完以上处理,剩下的就是主工程中的代码修改了。

Swift3.2Swift4.0的过程,比从Swift3.1Swift3.2的过程要麻烦一点,可是比当年从Swift2.3Swift3的过程要好太多了。app

下面我列举一下Swift3.2Swift4.0的改变(只是我项目中遇到的):框架

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

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

  • swift3使用#selector指定的方法,只有当方法权限为private时须要加@objc修饰符,如今全都要加@objc修饰符

  • 字体方面的一些重命名(NSFontAttributeName重命名为NSAttributedStringKey.fontNSForegroundColorAttributeName重命名为NSAttributedStringKey.foregroundColorNSStrikethroughStyleAttributeName重命名为NSAttributedStringKey.strikethroughStylesize(withAttributes:)方法重命名为size(withAttributes:))

  • ...

OCSwift4.0混编才是坑

因为历史缘由,我负责的项目,还有好大一部分OC的代码,新写的Swift须要被OC调用。因此,问题来了...

OC调用Swift4.0问题一:编译不经过

我在Swift4的代码中写了很多classextension,有些也给OC调用。在OC的代码中,咱们经过#import "ModuleName-Swift.h"导入了Swift文件,以给OC调用。若是是Swift3.2,一切都能正常工做,可是在Swift4.0上,编译通不过了。

一:在OC中调用一个Swift4.0类的方法(包括实例方法、static方法、class方法),你须要:

  • 在该Swift4.0类前加上修饰符@objc

  • Swift4.0类必须继承NSObject(不然,没法在前面加上修饰符@objc。固然,这里指的是普通类,@objc也是能够修饰UI开头的一系列UIKit框架下的UI类,只是修饰了这些类,不会产生什么影响)

  • 在须要调用的方法前加上修饰符@objc
    示例以下:

@objc class SampleObject: NSObject {

    @objc func sampleFunc  {
        print("sampleFunc")
    }
    
    @objc static func sampleStaticFunc  {
        print("sampleStaticFunc")
    }
    
    @objc class func sampleClassFunc  {
        print("sampleClassFunc")
    }

如此一来,即可在OC文件中调用,示例以下:

#import "OCSample.h"
#import "ModuleName-Swift.h"

@implementation OCSample

- (void)callSwiftFunc {
    // 调用实例方法
    SampleObject *object = [[SampleObject alloc] init];
    [object sampleFunc];
    // 调用static方法
    [SampleObject sampleStaticFunc];
    // 调用class方法
    [SampleObject sampleClassFunc];
}

@end

二:在OC中调用一个Swift4.0扩展的属性(包括实例属性、static属性、class属性)、方法(包括实例方法、static方法、class法),你有以下两种选择方式:

  • 在该Swift4.0扩展前加上修饰符@objc(这样的话,该扩展下的全部的属性、方法,均可被OC调用)。

示例以下:

@objc extension UIViewController {

    var name: String {
        reutrn "name"
    }
    
    static var staticName: String {
        reutrn "staticName"
    }
    
    class var className: String {
        reutrn "className"
    }
    
    func nameFunc() {
        print("nameFunc")
    }
    
    static func staticNameFunc() {
        print("staticNameFunc")
    }
    
    class func classNameFunc() {
        print("classNameFunc")
    }
    
}
  • 在须要的属性、方法前直接加上@objc修饰,也可达到目的。

示例以下:

extension UIViewController {

    @objc var name: String {
        reutrn "name"
    }
    
    @objc static var staticName: String {
        reutrn "staticName"
    }
    
    @objc class var className: String {
        reutrn "className"
    }
    
    @objc func nameFunc() {
        print("nameFunc")
    }
    
    @objc static func staticNameFunc() {
        print("staticNameFunc")
    }
    
    @objc class func classNameFunc() {
        print("classNameFunc")
    }
    
}
OC调用Swift4.0问题二:运行时找不到属性

这个问题藏得比较深,恰巧项目中有着相关的实现,让我看出发现这个潜在因素。
项目中有这么一种实现:有一个Swift4.0的类,是继承UIViewController的。而后我在OC里面对这个继承而来的UIViewController进行操做,我用了[viewController valueForKey:@"iconURL"]这一KVC方法去获取这个自定义UIViewController中的iconURL这一属性的属性值。这种方式,编译时是没法检查出问题的。可是在运行时,问题就来了,找不到这个属性。由于这个属性没有暴露给OC来进行调用。

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

示例以下:

class SampleViewController: UIViewController {
    @objc var iconURL: String?
}

除了在OC里经过valueForKey:方法调用到一些未通过@objc修饰的Swift4.0UI类的属性会致使crash。其余好比你在Swift4.0代码中,经过setValuesForKeys这种经过KVC来操做未通过@objc修饰的属性,也会致使crash

关于混编方面的更多信息

更多关于混编方面的内容,能够访问查看Apple官方提供的这篇文章:Using Swift with Cocoa and Objective-C (Swift 4),篇幅很多,不仅仅介绍了Swift4.0OC的混用,也介绍了与Capi的交互、还有更多关于@objc修饰符的用法。

关于Xcode9-beta的更多

Xcode9-beta局域网调试

要求

  • 必须是Xcode9-beta

  • iPhone系统需iOS11以上

操做

  1. Xcode9-beta菜单的Window选项中选择Devices and Simulators

  2. 经过链接线让你的Mac识别到你的iPhone

  3. Devices and Simulators面板的左侧Connected菜单中选择链接的设备,而后在顶部的DevicesSimulators选项中选择Devices(这里其实默认就是选择了Devices),最后勾选Connect via network选项。

来自stackoverflow回答

结束语

关于本文

  • 本文为做者这几天在Xcode9-beta以及Swift4.0方面的学习记录与分享,做者会视状况对内容进行补充。

  • 若是您在阅读本文中发现内容存在错误,但愿您积极指出。若是您有其余建议,也欢迎在评论去区留言。

  • 做者接受指正,可是但愿彼此之间保留敬意。

  • 欢迎转载,但请保留博文的原地址或者博文在简书上的地址。

关于本人

比起 微博@Jiar ,更喜欢 推特@JiarYoo ,求一波关注。?

微信订阅号

欢迎关注个人我的微信订阅号,我将不按期分享开发方面的干货。

Jiar's 微信订阅号

相关文章
相关标签/搜索