前言
很多iOS工程都是基于Object-C开发,再逐步向Swift演进,演进过程中不可避免要进行Swift混编。Swift模块需要支持LLVM Module规范,混编工程会遇到各种Module编译错误。这对于不熟悉的同学来说简直是灾难,严重影响开发效率。本文会介绍常见的Module编译错误,希望对大家有所帮助。
常见错误1:Could not build module xxx
当一个OC模块引用了Swift模块就产生了混编,混编的OC模块依赖链中的所有模块都必须满足LLVM Module Standard。如果AModule依赖了BModule,BModule不符合Module规范,构建会报 “could not build module BModule”的错误。
解决方案1:通过一下设置“Framework Module允许导入非modular的头文件” 临时跳过问题
Approach1:模块的Podspec中设置
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES
Approach2: 在Podfile里设置
post_install do|installer|
installer.pods_project.build_configuration_list.build_configurations.eachdo|configuration|
configuration.build_settings['CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES']='YES'
end
end
解决方案2:将BModule改造为符合LLVM Module标准(推荐)
常见错误2:No such module 'XXXModule'
原因1:A模块的依赖树里不存在B模块
如果“Pod A” 需要import “BModule”,“AModule”的Podspec描述文件需要声明对于BModule的依赖,否则就会报“No such module 'XXXModule'”错误。Cocoapod会将依赖关系转化成XCode的FRAMEWORK_SEARCH_PATHS或LIBRARY_SEARCH_PATHS,因此只需要检测xcconfig是否存在对应的配置,就能判断Podspec描述是否缺失依赖。
step 1: 打开A模块的xcconfig文件
Pods/Target Support Files/AModule/AModule.xcconfig
Step 2: 查看xcconfig里的frameowork和library配置,如果不存在是模块B,说明A模块的依赖树里没有B,需要再A模块的podspec文件中添加模块B的依赖。
LIBRARY_SEARCH_PATHS "${PODS_CONFIGURATION_BUILD_DIR}/B"
FRAMEWORK_SEARCH_PATHS ="${PODS_ROOT}/B"
原因2:BModule的Framework缺少modulemap文件
符合LLVM Module Standard的Framework里都会有一个.modulemap文件,如果缺少.modulemap文件说明Framework工程没有配置Define Module,同样会报上述的错误。
Step1:检查Pods目录里BModule的Framework是否包含正确的.modulemap文件
../Pods/Masonry/Masonry.framework
Step2: 如果当前版本有问题而历史版本正常,可以查看CocoaPods的Cache目录的各个版本,对比历史版本的Framework
../Library/Caches/CocoaPods/Pods/Release/Masonry/1.1.0-framework-DG-1-cbeee/Masonry.framework
下面是Masonry Framework解包的目录,目录中包含一个module.modulemap文件
framework module Masonry {
umbrella header"Masonry.h"
export *
module * { export * }
}
原因3:umbrella header中引用头文件不符合规范
umbrella header 会暴露给外部引用的public header。LLVM Module规定所有public header都需要使用“<A/A.h> ”的方式应用头文件。iOS工程中通常有下面几种引用方式,只有第一种是符合LLVM Module 规范的。
#import <A/A.h> right
#import "A/A.h" wrong
#import "A.h" wrong
#import <A.h> wrong
例如下面Masonry的umbrella header就不符合规范
解决方案是将头文件import方式从“#import "A.h" ”改为“#import <Masonry/xxx.h> ”
原因4:swift compiler incompatible
No such Module 'XModule'
Failed to build module 'AModule'; this SDK is not supported by the compiler (the SDK is built with 'Apple Swift version 5.3.1 (swiftlang-1200.0.41 clang-1200.0.32.8)', while this compiler is 'Apple Swift version 5.5 (swiftlang-1300.0.19.104 clang-1300.0.18.4)'). Please select a toolchain which matches the SDK.
本地调试时可能会遇到,比如AModule是静态库,AModule依赖了XModule, XModule是源码库。具体原因是AModule使用Xcode 12.4构建(Swift version 5.3.1),本地当前正在使用了Xcode12.5(Swift version 5.3.1 )调试。解决方案1是将本地使用的XCode版本先降到低于12.4.1 以下的版本。解决方案2是将AModule用新版本的XCode重新构建发布新版本。
常见错误3:Include of non-modular header inside framework module :
如果Framework Module ’AModule‘中加载了非AModule的头文件,比如AModule.h import了一个OC头文件X.h,而X.h头文件又Include某个C/C++ 的头文件
AModule.h:9:9: error: include of non-modular header inside framework module 'BModule.AModule'
解决方案:设置“Framework Module允许导入非modular的头文件”临时跳过问题
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES
常见错误4:Definition of 'AMoudle' must be imported from module 'BMoudle.xxxheader' before it is required
如果AMoudle和XModule的公共头文件同时引用了BModule的某个公共头文件,就会出现这个错误。
AMoudle_A.h 文件:
import <BModule/BModule_B.h>
XModule_X.m 文件:
import <BModule/BModule_B.h>
解决方案1:增加import <AModule/AMoudleXXX.h>
XModule_X.m 文件:
import <AModule/AMoudle_A.h>
import <BModule/BModule_B.h>
解决方案2:使用project引入模式调试pod
使用Development pod进行源码调试pod会遇到上述错误,而如果使用project引入模式调试pod可以隔离各个Project,就不会遇到这个错误。project引入模式类似swift package manager的edit模式。
自定义modulemap
OC Framework通过Cocoapod自定义modulemap
AModule是OC模块,可以在Podspec进行如下配置自定义modulemap
spec.preserve_path = 'Modules/module.modulemap'
spec.module_map = 'Modules/module.modulemap'
spec.xcconfig = { 'HEADER_SEARCH_PATHS' => '$(SDKROOT)/usr/include/libxml2 $(PODS_ROOT)/AModule/Modules/module' }
spec.pod_target_xcconfig = { 'HEADER_SEARCH_PATHS' => '$(PODS_ROOT)/AModule/Modules/module' }
Swift framework不允许自定义modulemap
[!] Using Swift static libraries with custom module maps is currently not supported. Please build `XXModule` as a framework or remove the custom module map.
总结
本文重点介绍了OC和Swift混编常见编译错误,如果你们的项目正从Object-C工程逐步向Swift演化,可以将本文作为参考。