Flutter iOS OC mixes Swift, encounters dynamic library and static library problems and fills the pit

I believe you have encountered the compilation problem of Flutter on iOS more or less. I wonder if the answer you get when you search for this convenient question is whether you need to clean or install it several times . In many cases, even if the problem is solved, it is still in Schrödinger's state, so this article also briefly records that in the development of Flutter, OC mixed with Swift encounters the problem of dynamic library and static library , I hope it will help you in the "circle".

image-20220422091858815

First of all, when I integrate a Swift plugin in an Objective-C project, what problems might I encounter?

As shown in the figure below, if you are an older Flutter project, there may be a problem that the swift plugin is not found .

image-20220422093205569

In response to this problem, it is generally recommended to add it under the Podfile file use_frameworks!, and sometimes it is recommended to add use_modular_headers!it. What is the role of these two flags?

target 'Runner' do
  use_frameworks! 
  use_modular_headers!
  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
复制代码

We know that the role of Podfile is to deal with CocoaPads, and use_frameworks!tell CocoaPods that you want to use Framework instead of static library, and because Swift does not support static library by default, there is a limitation that Swift must use Framework at first.

The difference between a static library and a Framework is:

  • *.a static library is similar to compiled mechanical code, source code and library code are integrated into a single executable file, so it is bound to the device architecture and does not contain resource files such as images;
  • Framework supports a format that encapsulates dynamic libraries, header files and resource files together. The simple understanding of dynamic libraries is that they will not be integrated together like static libraries, but dynamically linked at runtime or at runtime;

另外一个配置 use_modular_headers! ,它主要是将 pods 转为 Modular,因为 Modular 是可以直接在 Swift中 import ,所以不需要再经过 bridging-header 的桥接。

但是开启 use_modular_headers! 之后,会使用更严格的 header 搜索路径,开启后 pod 会启用更严格的搜索路径和生成模块映射,历史项目可能会出现重复引用等问题,因为在一些老项目里 CocoaPods 是利用Header Search Paths 来完成引入编译,当然使用 use_modular_headers!可以提高加载性能和减少体积。

继续回到问题上,我们在添加完 use_frameworks! 之后,有一定几率中奖各种 Undefined symbol 的错误问题,这时候不要慌,因为这是 Swfit 里有静态库导致。

image-20220422103410759

很明显 Swift 不支持静态库的行为不科学,所以从 Xcode 9 开始 Swift 就开始支持静态库,而 CocoaPods 1.9.0 开始,引入了 use_frameworks! :linkage => :static 来生支持有静态库和 Framework 的情况。

所以修改 use_frameworks 配置,增加 static 之后可以看到 Undefined symbol 的错误都消失了,但是运行之后,可能会喜提新的问题: non-modular header

image-20220422104501881

如果你去搜索答案,有很多答案会告诉你如下图所示,通过 Allow Non-modular Includes in Framework Modules 设置为 true 就可以解决问题,但是很明显这并不是正解,它更多适用于临时的紧急状体下

image-20220422105705987

当然,你也可以在出现问题的插件的 .podspec 下单独配置 ALLOW ,效果相同,更轻量级,但是这也只是临时解决方案。

s.user_target_xcconfig = { 'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES' }
复制代码

为什么说这种方式不靠谱,因为你不知道官方会什么时候删除这种允许,当然这个问题网友提供的解决方案其实千奇百怪:

  • 如果是 App 使用 dynamic framework 里的 header 导致错误,可以使用 #import "MyFile.h" 而不是 #import <MyFramework/MyFile.h>
  • #import语句移到 .m(而不是将其放在.h头文件中), 这样它就不会有包含 non-modular header 的问题,例如: github.com/AFNetworkin…
  • 重命名 header ,不要让 header 和模块名一样,变为 #import <FrameworkName/Header.h>
  • 在 build setting 配置 OTHER_SWIFT_FLAGS -Xcc -Wno-error=non-modular-include-in-framework-module 解决 Swift 的问题;

有可能它们都能解决你的问题,但是为什么呢?下次遇到这些问题要选哪个解决?

回归到我们的问题,其实我的问题关键是:不能在 Framework Module 中使用非 Modular 的 Header,也就问题是在 Framework Module 中加载了非当前 Module 的头文件,而由于 Header 是对外 public ,比如配置到了 s.public_header_files ,就会导致非 Modular 的 Header 也出现对外暴露的风险,所以我这边的解放方式也很简单:

* Put only the Plugin.h that needs to be disclosed in the , and use the non-Modular Header not to be public to the outside world, so as to comply with the specification and achieve successful compilations.public_header_files .

So the core here is: Do not refer to the OC header file that does not need to be exposed to the outside world in the Umbrella Header File as a sub-module, which also explains why the above #importstatement moved .mto The logic of solving the problem.

For example, sometimes you will also refer to some system C Modules, in fact, there will be similar problems in the process of Framework Moduleization.

So if you know why and how to solve it, you won't just rudely set it up through the LLVM configuration Allow Non-modular Includes in Framework Modulesto solve Schrödinger's problem.

In addition, you may also use it, such as emulator compilation prompts unsupport arm64, BITCODE failure, SWIFT_VERSION version conflict, etc.:

post_install do |installer|
  installer.pods_project.targets.each do |target|
    flutter_additional_ios_build_settings(target)
    target.build_configurations.each do |config|
        # building for iOS Simulator, but linking in an object file built for iOS, for architecture ‘arm64’
        config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64'
        #不支持 BITCODE
        config.build_settings['ENABLE_BITCODE'] = 'NO'
        #解决swift模块问题
        config.build_settings['SWIFT_VERSION'] = '5.0' 
    end
  end
end
复制代码

And of course, the last word: cherish your hair and stay away from the Swift mix .

Guess you like

Origin juejin.im/post/7089338745941393438