×

iOS 组件化-使用cocoapods集成实战演练

96
aron1992
2018.01.23 17:53 字数 1094

背景

之前写过一篇 iOS 组件化实现的一些思路总结 ,这篇作为续集,聊一聊使用 Cocoapods 在iOS平台组件化的实现和集成。

结果

本文中的两个例子可以在 YTThirdPlatformManager 项目中找到。

工具介绍

Cocoapods 是iOS/osx平台的开发库管理工具,简单的配置然后执行安装命令 Cocoapods 会自动去下载第三方库并且做好相应的配置,简化了引入第三方库的流程,让开发更简单高效,是iOS开发的必备工具,使用 Cocoapods 作为组件化的工具是一个不错的选择。

安装和设置

安装和设计可以参考这篇文章:
Cocopods安装和升级备忘录

实现

简单项目组件化

以一个测试模块解耦的场景实现简单项目的组件化,主要包含以下内容

  • Pod库项目创建
  • 最基础podspec文件的编写的解释
  • 客户端集成

创建项目

使用 pod lib create 创建项目,会遇到几个需要输入的地方,具体的解释看代码段中的注释

➜  DevPods pod lib create PTTestKit
Cloning `https://github.com/CocoaPods/pod-template.git` into `PTTestKit`.
Configuring PTTestKit template.

------------------------------

To get you started we need to ask a few questions, this should only take a minute.

If this is your first time we recommend running through with the guide: 
 - http://guides.cocoapods.org/making/using-pod-lib-create.html
 ( hold cmd and double click links to open in a browser. )

# 使用的语言
What language do you want to use?? [ Swift / ObjC ]
 > Objc

# 是否包好测试工程,指定YES用于模块化的解耦测试
Would you like to include a demo application with your library? [ Yes / No ]
 > 
yes

# 集成的测试模块,不需要指定None
Which testing frameworks will you use? [ Specta / Kiwi / None ]
 > None

# UI测试模块,不需要指定No
Would you like to do view based testing? [ Yes / No ]
 > No

# 指定类前缀
What is your class prefix?
 > PT

Running pod install on your new library.

podspec 文件编写

一个简单的 podspec 文件如下,具体字段的解释查看代码中的注释即可


Pod::Spec.new do |s|
  s.name             = 'PTTestKit'
  s.version          = '0.1.0'
  s.summary          = 'Wow PTTestKit.'

# This description is used to generate tags and improve search results.
#   * Think: What does it do? Why did you write it? What is the focus?
#   * Try to keep it short, snappy and to the point.
#   * Write the description between the DESC delimiters below.
#   * Finally, don't worry about the indent, CocoaPods strips it!

    # 长的描述信息
  s.description      = <<-DESC
Wow this is a amazing kit,
Enjoy yourself!
                       DESC

    # 提交到git服务区的项目主页,没提交可以指定任意值,但需要保留这一项,否则会报错
    # attributes: Missing required attribute `homepage`.
    s.homepage         = 'https://github.com/flypigrmvb/PTTestKit'
    # s.screenshots     = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
    # 授权文件
    s.license          = { :type => 'MIT', :file => 'LICENSE' }
    # 用户信息
    s.author           = { 'flypigrmvb' => '862709539@qq.com' }
    # 提交到git上的源码路径,没提交可以指定任意值,但需要保留这一项,否则会报错
    # attributes: Missing required attribute `source`.
    s.source           = { :git => 'https://github.com/flypigrmvb/PTTestKit.git', :tag => s.version.to_s }
    # s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'

    # 指定最低的ios版本
    s.ios.deployment_target = '8.0'

    # 源文件的路径
    s.source_files = 'PTTestKit/Classes/**/*'

    # 公共的头文件,按需设置
    s.public_header_files = 'PTTestKit/Classes/Public/**/*.h'
    # 私有的头文件,按需设置
    s.private_header_files = 'PTTestKit/Classes/Private/**/*.h'
    # 依赖的系统Framework,按需设置
    # s.frameworks = 'UIKit', 'MapKit'
    # 依赖其他的pod库,按需设置
    # s.dependency 'AFNetworking', '~> 2.3'
end

客户端集成

如果在创建Pod库项目的步骤集成了 Example 测试项目,在测试项目下的podfile默认包含了当前的Pod库项目

#use_frameworks!

target 'PTTestKit_Example' do
  pod 'PTTestKit', :path => '../'

  target 'PTTestKit_Tests' do
    inherit! :search_paths

    
  end
end

切换到测试项目目录下,运行 pod install 命令,完了之后测试项目集成了Pod项目。下面是在测试项目中使用Pod库项目中一些功能的简单例z

#import "PTViewController.h"
#import <PTTestKit/PublicFile.h>
// !!private header 可以导入
#import <PTTestKit/PrivateFile.h>
// !!报错
//#import <PTTestKit/ProjectFile.h>

@interface PTViewController ()

@end

@implementation PTViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self addActionWithName:@"Test" callback:^{
        NSLog(@"====");
    }];
    
    [self addActionWithName:@"PrivateFile" callback:^{
        [PrivateFile test];
    }];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [PrivateFile test];
}

@end

分模块组件化

以一个第三发组件集成的场景为例,实现分模块组件化,主要包含以下内容:

  • 创建核心模块
  • 创建子模块
  • 客户端集成

pod库项目的创建流程和简单模块组件步骤一致,不在赘述

podspec 文件编写

该文件创建了一个Core模块,用于存放抽象的接口、基类以及一些公用的工具类和头文件,以及几个子模块用于具体的第三方平台的实现。具体的内容可以查看以下代码中的注释内容


Pod::Spec.new do |s|
  s.name             = 'PTThirdPlatformKit'
  s.version          = '0.1.0'
  s.summary          = 'A short description of PTThirdPlatformKit.'

# This description is used to generate tags and improve search results.
#   * Think: What does it do? Why did you write it? What is the focus?
#   * Try to keep it short, snappy and to the point.
#   * Write the description between the DESC delimiters below.
#   * Finally, don't worry about the indent, CocoaPods strips it!

  s.description      = <<-DESC
TODO: Add long description of the pod here.
                       DESC

    s.homepage         = 'https://github.com/flypigrmvb/PTThirdPlatformKit'
    # s.screenshots     = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
    s.license          = { :type => 'MIT', :file => 'LICENSE' }
    s.author           = { 'flypigrmvb' => '862709539@qq.com' }
    s.source           = { :git => 'https://github.com/flypigrmvb/PTThirdPlatformKit.git', :tag => s.version.to_s }
    # s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'

    s.ios.deployment_target = '8.0'
    # 设置默认的模块,如果在pod文件中导入pod项目没有指定子模块,导入的是这里指定的模块
    s.default_subspec = 'Core'

    # 定义一个核心模块,用户存放抽象的接口、基类以及一些公用的工具类和头文件
    s.subspec 'Core' do |subspec|
        # 源代码
        subspec.source_files = 'PTThirdPlatformKit/Classes/**/*'
        # 配置系统Framework
        subspec.frameworks = 'CoreMotion'
        subspec.dependency 'SDWebImage'
        # 添加依赖的系统静态库
        subspec.libraries = 'xml2', 'z', 'c++', 'stdc++.6', 'sqlite3'
    end


    # 支付宝模块
    s.subspec 'AlipayManager' do |subspec|
        # 源代码
        subspec.source_files = 'PTThirdPlatformKit/AlipayManager/**/*'
        # 添加资源文件
        subspec.resource = 'PTThirdPlatformKit/AlipayManager/**/*.bundle'
        # 添加依赖第三方的framework
        subspec.vendored_frameworks = 'PTThirdPlatformKit/AlipayManager/**/*.framework'
        # 添加依赖系统的framework
        subspec.frameworks = 'CoreTelephony', 'SystemConfiguration'
        # 依赖的核心模块
        subspec.dependency 'PTThirdPlatformKit/Core'
    end


    # QQ模块
    s.subspec 'TencentManager' do |subspec|
        # 源代码
        subspec.source_files = 'PTThirdPlatformKit/TencentManager/**/*'
        # 添加资源文件
        subspec.resource = 'PTThirdPlatformKit/TencentManager/**/*.bundle'
        # 添加依赖第三方的framework
        subspec.vendored_frameworks = 'PTThirdPlatformKit/TencentManager/**/*.framework'
        # 添加依赖系统的framework
        subspec.frameworks = 'SystemConfiguration'
        # 依赖的核心模块
        subspec.dependency 'PTThirdPlatformKit/Core'
    end


    # 微博模块
    s.subspec 'WeiboManager' do |subspec|
        # 源代码
        subspec.source_files = 'PTThirdPlatformKit/WeiboManager/**/*'
        # 依赖的微博pod库
        subspec.dependency 'WeiboSDK'
        subspec.dependency 'PTThirdPlatformKit/Core'
    end


    # 微信模块
    s.subspec 'WXManager' do |subspec|
        # 源代码
        subspec.source_files = 'PTThirdPlatformKit/WXManager/**/*'
        # 依赖的微信pod库
        subspec.dependency 'WechatOpenSDK'
        subspec.dependency 'PTThirdPlatformKit/Core'
    end

end

客户端集成

podfile文件如下,可以导入主模块和任意的子模块的组合

#use_frameworks!
platform :ios, '8.0'

target 'PTThirdPlatformKit_Example' do

    pod 'PTTestKit', :path => '../../PTTestKit'

    # 主模块
    pod 'PTThirdPlatformKit', :path => '../'
    # 子模块快
    pod 'PTThirdPlatformKit/AlipayManager', :path => '../'
    pod 'PTThirdPlatformKit/TencentManager', :path => '../'
    pod 'PTThirdPlatformKit/WeiboManager', :path => '../'
    pod 'PTThirdPlatformKit/WXManager', :path => '../'
  
end

target 'PTThirdPlatformKit_Example_Developer' do
    pod 'PTTestKit', :path => '../../PTTestKit'
    
    pod 'PTThirdPlatformKit', :path => '../'
    pod 'PTThirdPlatformKit/AlipayManager', :path => '../'
    pod 'PTThirdPlatformKit/TencentManager', :path => '../'
    pod 'PTThirdPlatformKit/WeiboManager', :path => '../'
    pod 'PTThirdPlatformKit/WXManager', :path => '../'
    
end

遇到问题

开发中的Pod库依赖另一个Pod库的处理

参考: https://guides.cocoapods.org/syntax/podspec.html#vendored_frameworks
podspec文件添加添加

s.vendored_frameworks = 'PTDataModule/Frameworks/*.framework'

ARC环境中配置非ARC文件

需要用到podspec规则中的subspec和requires_arc
参考:
https://guides.cocoapods.org/syntax/podspec.html#requires_arc
https://guides.cocoapods.org/syntax/podspec.html#subspec

s.requires_arc = true

        # no arc files rules
        non_arc_files = 'PTDataModule/Classes/**/JSONKit.{h,m}'
        s.exclude_files = non_arc_files
        s.subspec 'no-arc' do |sna|
            sna.requires_arc = false
            sna.source_files = non_arc_files
        end

PrefixHeader

参考:
https://guides.cocoapods.org/syntax/podspec.html#prefix_header_file

podspec指定 PTDataModule-prefixheader.pch 文件

s.prefix_header_file = 'PTDataModule/SupportFiles/PTDataModule-prefixheader.pch'

PTDataModule-prefixheader.pch 文件内容

#import "UtilMacro.h"
#import "DebugConfig.h"
#import "TIMAdapter.h"

有个坑,使用pod install 之后自定义的pch文件在项目中找不到了,但是内容会添加到PTDataModule-prefix.pch文件中

可以使用这种方式,直接把需要导入的头文件使用参数的方式指定
参考:
https://guides.cocoapods.org/syntax/podspec.html#prefix_header_contents

spec.prefix_header_contents = '#import <UIKit/UIKit.h>', '#import <Foundation/Foundation.h>'

Pod 更新不成功问题

[!] The 'Pods-PTThirdPlatformKit_Example' target has transitive dependencies that include static binaries: (/Users/aron/PuTaoWorkSpace/Plush_devpods_developer/DevPods/PTThirdPlatformKit/Example/Pods/WechatOpenSDK/OpenSDK1.8.0/libWeChatSDK.a and /Users/aron/PuTaoWorkSpace/Plush_devpods_developer/DevPods/PTThirdPlatformKit/Example/Pods/WeiboSDK/libWeiboSDK/libWeiboSDK.a)

解决办法:

use_frameworks! (注释这个)

添加.a静态库和对应的头文件 PTBehaviorStat

s.public_header_files = 'PTBehaviorStat/Classes/**/*.h', 'PTBehaviorStat/vendor/**/*.h'
s.vendored_library = 'PTBehaviorStat/**/*.a'

pod lib create 创建库拉取GitHub模板数据慢的问题

--template 参数指定本地的 模板数据

Pod开发库依赖本地开发库的问题

参考:
https://stackoverflow.com/questions/16905112/cocoapods-dependency-in-pod-spec-not-working
比如Pod开发库A依赖Pod开发库B,依赖的信息填写如下即可,不需要指定路径获取其他信息

s.dependency 'DevLibB'

在测试工程或者客户端工程的的podfile文件中需要显示的导入DevLibB,这样即可

pod 'DevLibB', :path => '../../DevLibB'

源文件没有配置对会找不到

subspec.source_files = 'PTThirdPlatformKit/WeiboManager/**/*'

如果配置(如下所示)是错误的路径,在客户端是找不到这个类的,需要检查源文件配置的是否正确

subspec.source_files = 'PTThirdPlatformKit/WeiboManager111/**/*'

总结

以上是我在使用cocoapods做组件化集成实践的一些总结和遇到的一些问题,希望对有需要的朋友有帮助。

参考链接

cocoapods项目主页

iOS开发
Web note ad 1