让CocoaPods static library支持Module

让CocoaPods static library支持Module_第1张图片
1442384467493865.jpg

先说一点概念,心急的看官可以直接看分割线以下的部分

Module

Module是一种集成库的方式,在Module出现之前,开发者需要在引入库文件的同时引入需要使用的头文件,以保证编译的正常进行。但是每次引入库的时候都要导入一堆文件,看起来并不优雅。Module和Framework的出现让开发者极大程度上告别了这些不优雅的工作。从Xcode5开始,Apple对系统内置的动态库添加了Module的支持。Xcode6开始将Module开放给开发者,开发者可以让自己的库支持Module。

Module本质上是一个描述文件,用来描述Module中包涵的内容,每个Module中必须包涵一个umbrella头文件,这个文件用来#import所有这个Module下的文件,比如#import 这个UIKit.h就是一个umbrella文件。

Framework

Xcode创建的每个Framework中都包涵了一个自动生成的Module和一个umbrella文件,Module文件在工程中是不可见的,是在编译时生成的一个文件。我们可以在生成的.framework文件中找到module.modulemap这个文件,就是前文我们所说的Module。很多第三方的静态库Framework并没有使用Module,这也是我们不能在Swift中直接引入该静态库的原因。需要在bridge文件中import我们需要的静态库对应的头文件,然后才能在Swift中使用。使用Module的话我们可以直接优雅的在Swift文件前面写上import {Lib name}即可完成引用。framework的一个好处是让我们可以把.h文件module文件以及一些资源文件全部都放在.framework这个文件夹下,这样我们在引入的时候只关注这个文件夹就好了,而不用再去操心其他的文件。

Framework只是一种引入文件的方式,其包含的库文件可以是静态库,也可以是动态库,无论包含哪种库,使用方式都是一样的。在开发过程中,如果需要引入第三方的动态库,记得将动态库的Framework添加到Embedded Binaries中。动态库文件需要完整拷贝到.app中,因为动态库的link是在运行时进行的。

===不==知==道==华==不==华==丽==的==分==割==线===

Requirement

我们产品的架构正在向模块化的方向蹒跚前行,我们将不同的模块拆封成隔离的环境,以Framework的方式集成到工程中。Xcode默认的情况下引入一个在Header中引入了不属于任何Module的头文件的framework中的这个Header的情况下(这句话看起来可能比较难饶,来张图示意一下)


让CocoaPods static library支持Module_第2张图片
CF482C0A-7E57-4475-BB3E-F4194A525F97.png

如果此时图中的Our Framework的Header引入了Static Library的Header的时候,就会报一个non-modular header inside framework module的错误,意思是最好不要这么干。为什么是最好不要这么干?因为Build Settings提供了一个选项Allow Non-modular Includes In Framework Modules允许我们忽略掉这个错误。但是因为这个牵涉到环境的隔离,所以笔者认为这么做并不优雅。所以我最开始的时候把这个方案当成了下下策。然后我想到了另一个方案,CocoaPods提供了use_frameworks!选项,开启这个选项的时候,pods集成的所有的库都会以dynamic library framework的方式引入到工程中。问题又来了,由于目前我们的产品还有一大部分iOS7的用户,所以我们的App需要对这个万恶的版本进行支持,iOS7中并不能支持动态库。前面说到CocoaPods的use_frameworks!是以动态库的方式引入到工程的。期间我想了一个办法,就是手动将pods下target的Mach-O参数改为static library。然后发现,有一些我们引入的库本来就是以静态库的方式存在的,所以直接这么改会产生一些错误,并且在CocoaPods的github上某个issues中看到了开发者说use_framework!并不支持iOS8以下的工程,所以这个方案我并没有进一步的研究。最后我还是以错误为切入点,如何把non-modular变成modular。

What do we need to do?

基本思路就是在pod install之后在headers的目录下完成创建modulemap以及umbrella的创建,这样主工程就能检索到modulemap以及umbrella。我在podfile中添加了如下代码:

def generate_modulemap(name, path)
    f = File.new(File.join("#{path}/module.modulemap"), "w+")
    module_name = "#{name}"
    while(module_name["+"])
        module_name["+"] = "_"
    end           
    f.puts("module XB#{module_name} {")
    f.puts("    umbrella header \"#{name}_umbrella.h\"")
    f.puts("    export *")
    f.puts("}")
end

def generate_umbrella(name, path) 
    f = File.new(File.join("#{path}/#{name}_umbrella.h"), "w+")
    f.puts("#import ")
    Dir.foreach(path) do |filename|
        if filename != "." and filename != ".."
            f.puts("#import \"#{filename}\"")
        end
    end
end

post_install do |installer|
    require "fileutils"
    headers_path = "#{Dir::pwd}/Pods/Headers/Public/"

    installer.pods_project.targets.each do |target|
        target_header_path = "#{headers_path}#{target.product_name}"
        if File.exist?(target_header_path)
            filename = target.product_name
            if filename != "." and filename != ".."
                generate_umbrella(filename, target_header_path)
                generate_modulemap(filename, target_header_path)
            end
        end
    end
end

然后运行pod install,然后编译。Everything is prefect!

参考文章:http://blog.bjhomer.com/2015/05/defining-modules-for-custom-libraries.html

你可能感兴趣的:(让CocoaPods static library支持Module)