随着swift ABI的稳定,越来越多的开发者开始使用swift语言开发项目,但是由于大部分工具库也还是使用OC写的,因此我们不得不需要在项目中采用swift与oc混编。

在开发app项目时,swift与oc混编其实很容易,xcode会自动为我们建立一个桥接文件,这样我们就很容易的在swift中调用oc的方法或类对象。

但是在开发framework的时候,xcode不会为我们建立这个桥接文件,因此我们在framework中也就不能采用桥接的方式进行混编了。

framework中实现混编的方式有两种:

第一种:也是最简单的一种,就是将oc的头文件暴露出来,并在framework的头文件中导入。(缺点:我们不想对外暴露的类或方法,不得不暴露出去)

这样一来,我们就可以在swift中直接引用oc的类了

第二种:配置modulemap,这个方法可以避免我们不得不对外暴露我们的oc类。

(1):在项目中创建module.modulemap文件,具体内容如下:

module MyObjcFramework {
header "oc/MyObjc.h" // 这里的路径是相对于module.modulemap的所在目录
export *
}

这里我们定义了一个MyObjcFramework的模块,也就是提供给swift导入的模块名称。

header是该模块的头文件,如果你需要了解关于module的更多详细信息,可以查看这篇文章,或者Clang Module's documentation

(2):配置Swift Compiler - Search Paths  Import Paths:

这里是为了告诉编译器,我们自定义的module所在路径,这里指定了项目的根目录:$(SRCROOT),当然你也可以指定的更具体,像下面这样:

然后我们就可以在swift中导入我们自定义的module,从而引用oc类。

import Foundation
import MyObjcFramework // 导入自定义模块 public func testPrint(_ str: String) {
let objc = MyObjc()
objc.printStr(str)
}

以上两种方法解决了我们在使用swift开发framework时与oc混编的问题,一般常用第二种方式,不过依具体情况决定。

另外说一下swift开发framework与OC的区别,使用swift开发framework导出的framework文件中的Modules目录下多出一个xxx.swiftmodule包,这个是swift提供的公共组件接口定义。

我们看一下这个包里都有哪些文件:

swiftdoc应该是api文档的描述。swiftmodule定义了MyKit组件的api公共接口。

Project里的文件如下:

另外当你在Build Settings中设置Build Libraries for Distribution = YES时,再次Build你的framework后,在xxx.swiftmodule包中会多出一个swiftinterface文件。

这个是swift5.1之后对swiftmodule的补充,它们的目的都是为了实现编译一个向后兼容的二进制库。

下面是重点,重点,重点。

在合并模拟器与真机的二进制库时,不能像OC那样简单的将合并后的二进制文件替换就可以了,因为swift中模块定义的公共接口都是在swiftmodule中定义的,而它们也是区分架构的,因此你需要将合并后的二进制文件替换之外,将不同架构的swiftmodule和swiftinterface文件拷贝到当前framework/Modules/xxx.swiftmodule包中。

当然还有另外一种合并多平台架构的方式,那就是xcframework,但是我却在这里遇到了一个问题,那就是在swift于OC混编时使用modulemap时生成的xcframework,导入到实际app工程时报错,下面是具体的错误:

MyHandler就是在swift编写的framework工程中利用module.modulemap对OC文件的一种引入方式,对应的modulemap如下:

module "MyHandler" {

    umbrella header "./src/oc/MyTextHandler.h"

    export *
module * { export * }
}

使用MyHandler的地方:

import UIKit
import DrFlexLayout_swift
import MyHandler public class MViewController: UIViewController { let text: String
let handler: MyTextHandler public init(text: String) {
self.text = text
self.handler = MyTextHandler(prefix: "T")
super.init(nibName: nil, bundle: nil)
}
}

framework的工程可以正常编译通过,但是在合并成xcframework后,将xcframework导入到app工程后编译就会包上面的错误。

这个问题我暂时没办法解决,希望知道的解决方法朋友在下面留言告诉我一下,不胜感激!

难道就不能使用xcframework了吗?当然是可以使用了,前面我们提到过在swift编写的framework中混编OC或c时,还有另一种方法,那就是将OC或c的头文件暴露在framework工程的头文件中,这样生成的xcframework就可以正常使用了,但是缺点自然是有的,这也是没办法的办法。

感谢@ronzheng的帮助,以上的问题已经得到解决。解决办法是将import MyHandler替换成@_implementationOnly import MyHandler即可。我遇到的这个问题也有其他人同样遇到了,可以看这里查看,关于@_implementationOnly的作用没有官方解释,不过我推测就是将被修饰的导入的模块私有化,在使用中我同样遇到了一些其他问题,在评论中有提到。因此在使用@_implementationOnly导入内部私有模块时,最好放在内部类中使用。

最新文章

  1. 你所不知道的linq
  2. HiKariCP的数据源配置
  3. aliyun阿里云Maven仓库地址
  4. git提交远程仓库命令
  5. php开发环境
  6. ARC————自动引用计数
  7. 嵌入式 hi3518平台获取网关
  8. sql server 2008 查询语句的红色波浪线
  9. 系统调用和中断处理的异同(以Linux MIPS为例)
  10. android生成apk包出现Unable to add "XXX" Zip add failed问题
  11. 自己编写的 C++ 超轻量级日志类
  12. 初学时遇到的小问题Your content must have a ListView whose id attribute is 'android.R.id.list'
  13. 小白在 Eclipse如何避免启动时自动building workspace和validating
  14. Linux环境——MySQL安装及配置(8.0版本)
  15. node.js使用免费的阿里云ip查询获取ip所在地
  16. IntelliJ IDEA 破解方法
  17. 【洛谷p1066】2^k进制数
  18. 在iOS 8及以后使用UIAlertController 等各种弹出警告通知
  19. iOS中堆和栈的区别
  20. VFL使用

热门文章

  1. C# SMTP发邮件不支持465端口的解决方案
  2. 栈——stack的用法
  3. 与 Flutter 共创未来 | Flutter Forward 活动精彩回顾
  4. P4349 [CERC2015]Digit Division
  5. [EULAR文摘] 脊柱放射学持续进展是否显著影响关节功能
  6. 封装avalonia指定组件允许拖动的工具类
  7. LeetCode-593 有效的正方形
  8. Vue2安装less版本过高问题,需要降级
  9. mysql的七种基本关联查询方式
  10. Android 之Map容器替换 SparseArray,ArrayMap,ArraySet