xcframework

1,393 阅读9分钟

1 什么是xcframework 以及 为什么要使用xcframwork

  XCFramework (framework的增强版)

    1. 苹果官方推荐,支持的,可以更加方便多个平台和架构的分发二进制库的格式。
    1. 需要xcode11以上支持
    1. 在2019年提出的framework的另一种先进格式。
  1. 4 不用剥离架构

  2. 5 m1的电脑 framework 模拟和真机不能合并,只能使用XCFramework

  3. XCFrameworks 和之前 Framework 对比 : :juejin.cn/post/684490…

2 怎么生成xcframwork(源码/framework/.a)

  1. 源码:

    1.     直接用xcode或xcodebuild命令
  2. Framework

 xcodebuild -create-xcframework -framework iphonesimulator/NvStreamingSdkCore.framework -framework NvStreamingSdkCore.framework -output NvStreamingSdkCore.xcframework
xcodebuild -create-xcframework -library <path> [-headers <path>] [-library <path> [-headers <path>]...] -output <path>

  

  d. 生成带有dsym文件的xcframework. 坑: -debug-symbols 参数需要用全路径,-framework 不用全路径

xcodebuild -create-xcframework -framework iphonesimulator/NvStreamingSdkCore.framework -debug-symbols /Users/shan/Downloads/NvStreamingSdk_IOS_3.8.2.17_20230707_165743/lib/ios/iphonesimulator/NvStreamingSdkCore.dSYM  -framework NvStreamingSdkCore.framework  -debug-symbols /Users/shan/Downloads/NvStreamingSdk_IOS_3.8.2.17_20230707_165743/lib/ios/NvStreamingSdkCore.dSYM -output NvStreamingSdkCore.xcframework

3 组件如何 Module 化(modular)

关于Module的参考:

   百度 App Objective-C/Swift 组件化混编之路(二)- 工程化 : xie.infoq.cn/article/7e2…

  使用自定义 Module 解决 Objective-C 库的引用 : chaosky.tech/2020/08/03/…

  Swift与Objective-C混编时,我们是如何将编译时间优化了35%?:blog.csdn.net/csdnnews/ar…

1 源码如何 Module 化

2 Framework如何 Module 化

3 .a 如何 Module 化

  对应demo 参见目录:/Users/shan/Documents/meipian_f

  测试环境:

  macOS12.6 Xcode13

  SWift类如何引用 .a 静态库:

  方法1:

    如果是主工程,在xxx-Bridging-Header.h 添加静态库的头文件即可引用;如果是pod库,则可以在和pod库同名的头文件中,添加静态库的头文件即可引用。

  方法2:

    对静态库进行包装,参见demo: (github.com/chaoskyx/De…. , 文章: github.com/xwal/xwal.g… chaosky.tech/2020/08/03/… ),; GDTMobSDK是 .a 静态库,GDTPackage对GDTMobSDK封装之后,可以直接在Swift文件中 import GDTMobSDK

  方法3:

    把 .a 静态库转换为framework,然后进行module化,可行; 还可以进一步把module后的framework转为xcframework. 转化方法参见:www.cnblogs.com/yunteng/p/8… 或自行搜索;

    注意:去掉.a后缀的时候,要右击显示间接中去掉

    参见demo: 点a静态库转framework

  方法4:

    .a静态库 难道不能直接 module化?module化之后,直接在swift文件中 import ,可行

    参见demo: 点a静态库直接module化, 这里最坑的地方在于, 自定义的modulemap要通过s.source_files引入,通过s.module_map 无法引入,

    // modulemap要配置在source_files中,配置在module_map中不起作用

    

    因为这个问题花费了 我两天时间,直到看到这篇文章: 制作自己的CocoaPods公开库(涉及modulemap) www.jianshu.com/p/88ff2e01d…

    期间,在stackoverflow 上找了很久,也没找到解决办法:

    找过的资料:

    ```

    How to import Objective C static library to Swift Framework? (stackoverflow.com/questions/3…)

    ```

    资料都说了怎么定义modulemap,但是都没有说,怎么通过pod引入自定义的modulemap,

    后来在官方资料上看到对module_map的解释,之前没仔细看:

    

    guides.cocoapods.org/syntax/pods…

    也就是module_map 只对 framework 才有效,对.a静态库无效,

    在官方文档上没有找到,对.a静态库无效 自定义modulemap的说明。

  方法5:

     .a静态库直接转为xcframework, 然后module化;可以解决 模拟器arm64架构和真机arm64架构和不能合并的问题; 参见demo: 点a静态库转xcframework的module化,

  遇到的奇怪问题:

    1. 如果libNvPhotoAlbumHelper.xcframework 同目录下有同名的framework libNvPhotoAlbumHelper.framework ,并且framework 有Modules/module.modulemap,在podspec中直接这么写,如下图,可以编译通过, 在主工程中直接@libNvPhotoAlbumHelper 方式引入,也可以正常使用,编程通过。 为什么?为什么?

    

    如果删除同目录下的libNvPhotoAlbumHelper.framework,就会报错;但是只保留libNvPhotoAlbumHelper.framework中的Modules也不会报错。

    1. 如果libNvPhotoAlbumHelper.xcframework 同目录下,不存在同名的framework libNvPhotoAlbumHelper.framework ,podspec中这么写也不会报错:

    

    但是,奇怪的是 自定义的"Module/Lottie.modulemap",并没有用,有用的是自动生成的NvPhotoAlbumH.modulemap

    截屏2022-10-26 15.51.14

    哪怕删除Lottie.modulemap 文件中的所有内容,也不影响;但是不通过source_files引入Lottie.modulemap就不会生成自动NvPhotoAlbumH.modulemap;

    也就是只要通过 s.source_files 引入xx.modulemap 文件,就会触发自动生成的功能。

    1. 如果在上面的基础上传加了 s.module_map,podsepc如下:

    

    这时也会自动生成NvPhotoAlbumH.modulemap了,但是生成的NvPhotoAlbumH.modulemap中的内容就是引入的Lottie.modulemap的内容;

     如果source_files中不引入modulemap,只在module_map中引入,会发现没有用;

    modulemap文件 注意:

    module * { export * } 和 export * 的区别

    可以发现module * { export * } 会直接把所有 .h 中的方法, 直接导入到当前的 module 中。 另外只有umbrella才可以使用module * { export * }。

4 胖二进制文件和瘦二进制文件

.a库可以把模拟器库和真机库合并为一个库,打包的时候不用做处理理,不论是直接添加到主工程还是通过pod库引用, xcode会自动去掉不需要的架构;

但是m1电脑出现之后,模拟器库也需要arm64, 也就是真机的arm64和模拟器的arm64 不通用,所以需要合并,通过下面命令合并的时候会报错:


lipo -create libNvPhotoAlbumHelper.a iphonesimulator/libNvPhotoAlbumHelper.a -output libNvPhotoAlbumHelper_fat.a


fatal error: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/lipo: libNvPhotoAlbumHelper.a and iphonesimulator/libNvPhotoAlbumHelper.a have the same architectures (arm64) and can't be in the same fat output file

无法合并,所以如果想提供一份在m1电脑上,即能在模拟器运行,又能在真机运行的.a文件是做不到的。但是在非m1电脑上是可以的。

对与m1电脑上 ,想即能在模拟器运行,有能在真机运行, 只能把.a 转为xcframewwork

由于NvStreamingSdkCore.framework包含模拟器和真机架构,所以直接拖到主工程,在xode13上会报一下错误:

注意需要设置embed值如下:

但是上面的错误在xode14上,不会出现,直接可以编译成功,可以直接在真机或模拟器上运行, 但是不能上传AppStore,上传App Store时会报错。

在Xcode13以及一下,用pod引入的时候,为什么动态framework即使包含模拟器和真机架构,也不会报错,这是为什么呢? 因为pod帮你做了这件事,在pod install之后,在工程配置中会生成如下配置:

这是在编译时会执行的脚本,

找到对应的文件,我们可以看看脚本内容:其中有一个方法,这个方法就是去掉真机架构或模拟器架构的:

调用的地方如下:

可以看到只有动态库才会调用

qiyukf.com/docs/guide/…

对于静态库.a 或 静态库framework, 即使是模拟器和真机合在一起也是可以直接运行的,

比如 libNvPhotoAlbumHelper.framework 直接拖入主工程是可以运行的

对于没有modulemap的framework, 可以自己添加一个,进行module化,参见:Swift:巧用module.modulemap,告别Bridging-Header.h

juejin.cn/post/713972…

XCFrameworks 和之前 Framework 对比 : juejin.cn/post/684490…

5 参考:

   iOS 中的库与框架:kingcos.me/posts/2019/…

   理解Objective-C的Modules : www.getit01.com/p2019010125…

  

   整理一下脑子里混乱的Architectures : juejin.cn/post/703478…

  

   iOS静态库和动态库的底层原理 : www.jianshu.com/p/d34081fa0…

  

   使用 Xcode 制作 Framework 与 XCFramework : www.jianshu.com/p/14f2e2236…

  

  

   细说iOS静态库和动态库: juejin.cn/post/684490…

  

   iOS开发进阶八:Module(模块) : www.jianshu.com/p/d7c99187d…

  

   Swift和Objective-C混编在有赞移动的实践 原创 : blog.51cto.com/u_15127581/…

  

   从零开始编写iOS混编SDK(下) : www.jianshu.com/p/436869f54…

  

   百度 App Objective-C/Swift 组件化混编之路(二)- 工程化 : xie.infoq.cn/article/7e2…

  

  

  

   使用自定义 Module 解决 Objective-C 库的引用 : chaosky.tech/2020/08/03/…

  

   understand-ios-library-and-framework : chuquan.me/2021/02/14/…

  

   京东App Swift 混编及组件化落地 : developer.jdcloud.com/article/196…

  

   Swift 关于 module.modulemap 使用 : www.jianshu.com/p/ce49d8f32…

  

   Swift Framework 自定义 Module : www.jianshu.com/p/b4f88651f…

  

   modulemap : www.jianshu.com/p/22f6c23c3…

   ```

  

   modulemap编译

  module中C和OC要分开编译, C++可以和C一起编译, C++也可以和OC一起编译。

  当OC 和C 一起需要添加requires objc

  module OCTest {

   requires objc

   header "OCTest.h"

   export *

   umbrella "Subs/OCSubs" // 单独把 Subs 中的 OC 文件, 单独列出来, 否则会编译失败

   module * { export * }

   }

  由于Swift不能直接调用C++代码, 所以一般会导出C和OC文件。

  Swift中不能直接调用C++代码,但是可以调用c代码, 通过void*生成c++对象, 通过c来调用C++

  作者:JerrySi

  链接:www.jianshu.com/p/22f6c23c3…

  来源:简书

  著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  

   ```

  

   #

  

  

   各种各样的modulemap(modulemap language:swift) github.com/search?utf8…

6 私有库打包怎么自动生成xcframework

  1 自己写脚本 2 修改pod package 3基于carthage 4 基于SPM

  基于carthage:

   1. 用carthage,如果库本身就支持carthage,可以直接用carthage 命令直接生成

  2. 如果不支持carthage,只支持pod, 可以用:

   1. 直接用pod, 以SDWebImage为例

   新建工程SDWebImageDemo, podfile 如下

  

  然后执行命令

  ```

  carthage build --platform iOS --no-skip-current --use-xcframeworks

  ```

  会报错:

  ```

  Dependency "SDWebImageDemo" has no shared framework schemes for any of the platforms: iOS

  ```

  报错的原因是:pod默认生成的是 .a 不是framework,

  

  也就是默认是下图中的2, 但是我们需要的是1

  

  怎么让pod库,默认生成1呢?

  在 podfile中加入 use_frameworks! 就可以了

  

  加入之后,pod install 之后

  

  发现已经变了,

  这时 在执行命令

  ```

  carthage build --platform iOS --no-skip-current --use-xcframeworks

  ```

  发现还是报错

  ```

  Dependency "SDWebImageDemo的" has no shared framework schemes for any of the platforms: iOS

  ```

  需要点击 Manage Schemes...

  

  选中Shared

  

  然后就可以。

  但是还有个问题,这么编译出来的xcframework是动态库,

  怎么编译为静态库呢;

  有三种方法:

  1 手动修改 ,然后编译

  

  2 在podspec 中添加

   s.static_framework = true

  3 在podfile 文件中添加,把 多有 pod库都修改为静态库

  ```

  post_install do |installer|

   installer.pods_project.targets.each do |target|

   target.build_configurations.each do |config|

   config.build_settings['MACH_O_TYPE'] = 'staticlib'

   end

   end

  end

  ```

  然后就可以生成静态xcframework 了

  除了用carthage生成xcframework外,还可以用脚本生成,网上有很多,感兴趣的可以去看看,

  用脚本生成,需要先配置工程,比较麻烦,所以我就不用了

  最近发现了可以根据podsepec直接生成xcframework的工具,类似pod package

  cocoapods-xcframework : github.com/TyrantDante…

  可能存在的问题

  swift oc库混合编译问题

7 私有库规范

  pod子spec的问题,依赖问题,怎么生成xcframework

  第三方sdk不支持arm64模拟器,podsepc需要加,但是自己的源码的pod spec可以不加

  最小app

  1 业务库之间不能有依赖

  2 不能有子podspec

   3要能@import 也就是能module化