Bootstrap

cocoapods 的主模块如何判断子模块有没有被加载?

当我们在制作 cocoapods 库时,有时候需要分成和,主模块负责提供主要的功能,使用者可以按需引用子模块。

比如 ,我们在使用时,首先需要引用主模块 ,然后根据产品需求添加分享渠道的子模块。

# 主模块(必须)
pod 'mob_sharesdk'

# 用户按需添加子模块
pod 'mob_sharesdk/ShareSDKPlatforms/QQ'
pod 'mob_sharesdk/ShareSDKPlatforms/SinaWeibo'

那主模块是如何知道子模块有没有被用户引用呢?

根据 framework 来判断

在 ShareSDK 和 文件当中,有这样一行注释:

各个平台:每个平台都必须要有ShareSDK.bundle和对应的Connector

下面是 文件 QQ 和 SinaWeibo 模块的部分设置:

# QQ
sp.subspec 'QQ' do |ssp|
    ssp.vendored_frameworks = 'ShareSDK/Support/PlatformSDK/QQSDK/TencentOpenAPI.framework','ShareSDK/Support/PlatformConnector/QQConnector.framework'
end

# SinaWeibo 精简版
sp.subspec 'SinaWeibo_Lite' do |ssp|
    ssp.vendored_frameworks = 'ShareSDK/Support/PlatformConnector/SinaWeiboConnector.framework'
end

通过观察发现,各个子模块都会依赖一个对应的 。所以我猜想 ShareSDK 是根据是否能够加载对应的 framework,来判断是否加载了子模块。

如何判断能否加载到指定的 framework 呢?

在 Objective-C 中,可以通过 来判断:

#if __has_include()
SomeFeature *h = [SomeFeature new]
#endif

在 Swift 中,可以通过 来判断:

#if canImport(MyFramework)
import MyFramework
#endif

根据子模块的宏来判断

在 podspec 文件中,可以通过 修改的编译选项,给模块添加条件编译的定义。

在 Objective-C 文件中,条件编译所用到的宏定义,是通过: 的 来定义的。

在 Xcode 8 之后,在 Swift 文件中使用到的宏,是通过 的 去定义的,直接定义添加宏名称即可。

我们可以通过在子模块中添加特定的宏,然后在主模块中判断是否有对应的宏定义,来判断是否加载了子模块。

假如,我们要自己实现一个 的框架,核心模块是 ,有 和 两个子模块,分别给子模块添加一个特殊的宏定义: 和 。

podspec 文件的配置如下:

# 核心模块
s.subspec 'Core' do |sp|
    sp.source_files = 'iShareSDK/Classes/Core/**/*'
end

# 可选的子模块
s.subspec 'Platforms' do |sp|

    # QQ
    sp.subspec 'QQ' do |ssp|
        ssp.source_files = 'iShareSDK/Classes/QQ/**/*'
        ssp.dependency 'iShareSDK/Core'
        
        ext.pod_target_xcconfig = {
            'SWIFT_ACTIVE_COMPILATION_CONDITIONS' => 'iShareSDK_Platforms_QQ',
            'GCC_PREPROCESSOR_DEFINITIONS' => 'iShareSDK_Platforms_QQ=1'
        }
    end

    # WeChat
    sp.subspec 'WeChat' do |ssp|
        ssp.source_files = 'iShareSDK/Classes/WeChat/**/*'
        ssp.dependency 'iShareSDK/Core'
        ext.pod_target_xcconfig = {
            'SWIFT_ACTIVE_COMPILATION_CONDITIONS' => 'iShareSDK_Platforms_WeChat',
            'GCC_PREPROCESSOR_DEFINITIONS' => 'iShareSDK_Platforms_WeChat=1'
        }
    end
end

在主模块中,我们就可以根据是否有子模块中的宏定义,来判断是否加载了子模块了。

public class iShareSDK {
    
    /// 处理所有平台的分享事件
    /// - Parameter platform: 分享渠道
    public func share(platform: Platform) {
        switch platform {
        case .QQ:
            #if iShareSDK_Platforms_QQ
            QQ.handleShare()
            #else
            debugPrint("未加载 QQ 模块")
            #endif
        case .WeChat:
            #if iShareSDK_Platforms_WeChat
            WeChat.handleShare()
            #else
            debugPrint("未加载 WeChat 模块")
            #endif
        }
    }
}

总结

在制作 cocoapods 库时,有时需要分成多个,而且是用户按需进行加载的,我们可以通过两种方式来判断用户是否加载了特定的子模块:

参考