美文网首页iOS开发技巧ios专题第三方库
CocoaPods 动/静态库混用封装组件化

CocoaPods 动/静态库混用封装组件化

作者: heroims | 来源:发表于2017-08-20 21:57 被阅读2928次
DingTalk20170818200900.png

动/静态库混用

pods的动静态库混用,相信大多数人一想到就会头皮发麻,体会过的应该都懂,那种无助感。。。。

问题

大型项目里来个尝试性swift过渡,首先就是pod加use_frameworks!来支持pod动态库,接着就是一大片的不支持动态库pod提示error,源码上看不dependency依赖静态库pod其实是不会有问题的,如果你的pod全是源码型也不会有任何问题!

ps:其实我感觉这是bug。。。压根就不是啥问题

正常的pod当静态库用的时候vendored_library,vendored_frameworks两个属性搞定一切。。。

解决

未来

虽说是未来,但我估计也就只有几个月的事吧,CocoaPods马上就要可以加动静态库标签了!长期关注pods源码的估计不以为然,上个月就定下来1.4.0发布,然而现在是1.3.1。。。。。
那老哥们PR了好久https://github.com/CocoaPods/Core/pull/386
看源码写的用法每个pod是可以定义static_framework=true属性,这样其实对简单的小项目半点作用都没有,但大型项目简直是救世的存在!承接上边的问题,对指定pod加静态库标签瞬间解决!

补(已经完美现在1.4.0,唯一要注意的是依赖的其他pod也要保证是静态库或加static_framework为ture,但下个版本就修复不用管了只是还没发布)
Pod::Spec.new do |s|
s.name                  = 'XXXThirdPartSocial'
s.version               = '1.0'
s.summary               = '第三方社交模块'
s.homepage              = 'xxx.xxx.xxx'
s.license               = { :type => 'MIT'}
s.author                = { 'xxx' => 'xxx@163.com' }
s.source                = { :git => 'xxx.xxx.xxx', :tag => "#{s.version}" }
s.platform              = :ios, '7.0'
s.source_files          = ''
s.requires_arc          = true
s.ios.dependency    'UMengUShare/Social/ReducedQQ'
s.ios.dependency    'UMengUShare/Social/ReducedWeChat'
s.ios.dependency    'UMengUShare/Social/ReducedSina'
s.static_framework  =  true
end

现在

接着扯一下现在的解决方案,首先我们针对的就是pod提供framework和.a的情况,核心问题其实是自己怎么建立对framework和.a的支持。
把dependency一些静态库的pod拍平就是现在的解决方法,自己建pod,保证一层支持framework和.a,另外如果实在自己的pod里dependency的静态库pod,这个时候比较好的选择是建立subspec,直接subspec里面封装对静态库的支持。
这里支持的时候要分为3类,先放一个友盟分享的例子:
友盟的framework 和.a都是静态库

Pod::Spec.new do |s|
s.name                  = 'XXXThirdPartSocial'
s.version               = '1.0'
s.summary               = '第三方社交模块'
s.homepage              = 'xxx.xxx.xxx'
s.license               = { :type => 'MIT'}
s.author                = { 'xxx' => 'xxx@163.com' }
s.source                = { :git => 'xxx.xxx.xxx', :tag => "#{s.version}" }
s.platform              = :ios, '7.0'
s.source_files          = ''
s.requires_arc          = true
#s.ios.dependency   'UMengUShare/Social/ReducedQQ'
#s.ios.dependency   'UMengUShare/Social/ReducedWeChat'
#s.ios.dependency   'UMengUShare/Social/ReducedSina'
s.subspec 'XXXThirdPartSocialVendor' do |sss|
sss.source_files            = ''
sss.resource                = 'UMSocialSDK/UMSocialSDKPromptResources.bundle'
sss.ios.vendored_frameworks = 'UMSocialSDK/UMSocialCore.framework','UMSocialSDK/UMSocialNetwork.framework'
sss.ios.vendored_library    = 'SocialLibraries/QQ/libSocialQQ.a','SocialLibraries/Sina/libSocialSina.a','SocialLibraries/WeChat/libSocialWeChat.a'
sss.ios.public_header_files   = 'SocialLibraries/**/*.{h}'
sss.ios.library  = 'sqlite3'
end
end
DingTalk20170817182046.png
动态库Pods封装.a

对.a封装的时候vendored_library属性对应.a,然后看看依赖啥系统库在library,frameworks里加上,最后就是.h,如果你不想暴露的话public_header_files 里加完就不用管了,如果想要暴露给别人调用,只能source_files里再加一遍.h。
上面例子中XXXThirdPartSocialVendor里的source_files为空,但其实.a里的东西你是可以调用的,原因是友盟在他的framework里的头文件引用了.a的头文件,间接让.a的.h公开,这问题在我看来感觉是个bug。。。
所以不想在source_files里再写一遍的也可以建个.h引用一遍所有.a的头文件,最后source_files写你自己的.h,但这只是保证我到处可以通过引用自己的头文件实现方法调用,并不能单个引用对应.a的头文件

动态库Pods封装静态Framework

对静态的Framework封装的时候可以说是最舒服的了,vendored_frameworks加上去基本就万事大吉了,至于依赖啥系统库加library,frameworks这件事,亲测有的时候并不需要!

动态库Pods封装动态Framework

对于动态的Framework封装,我不说估计大家也基本能猜到吧!这就是最难受的,具体情况具体分析,不同情形下用不同套路,就算不用pod也让你很不爽,这里我拿环信客服SDK来讲!

DingTalk20170817183749.png
不用pod你要手动把这SDK拖到上边Embedded Binaries位置头文件才能引用,这个是苹果现在引用动态Framework的套路。。。好烦!
下面讲一下pod怎么搞,如果单纯framework做pod,首先public_header_files要指定xxx.framework/Headers/{.h}不然你头文件找不到,其次source_files里看具体编译情况决定加不加xxx.framework/Headers/{.h},然后就是比较普通的地方vendored_frameworks指定好完事大吉!source_files这个加了的时候还有一个前提就是Framework内引用全是""不能<>,所以大部分情况source_files不加
另一种混合使用感觉这才是最常见的
DingTalk20170818125422.png
这时候不要指定Framework的public_header_files,写一个自己的头文件引用类,把想公开可以调用的在这里#import <xxx.framework/xxx.h>,只能间接把那些搞出去,起作用的只有vendored_frameworks

动态库Pods封装资源文件的调用

高能预警!超级天坑降临!
当你use_frameworks!这么一下你如果自定义的pod有关于resource或resource_bundle的话应该会发现真正的末日降临了,之前的资源全读不出来了!


DingTalk20170818141324.png

一张图片告诉你发生了什么,pod构建动态库的时候你的资源文件都在Framework里!
现在的选择变成:要么资源文件放外面单独加,要么改代码。。。。就问你坑不坑?
放外面单独加我这就不说了太简单,代码写的话其实也要看本身代码的结构什么样,如果像我举例中的SDK基本没救,没有统一的地方获取NSBundle,也没对bundle名称做统一,更没对UIImage设置加扩展!
下面简单说下调用方法

NSString * bundleName=@"Frameworks/xxx.framework/xxx.bundle";

NSString * bundlePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent: bundleName];

NSBundle *bundle= [NSBundle bundleWithPath:bundlePath];

//本地化(拖入.lproj 文件夹即可)
NSString *localizedString=[bundle  localizedStringForKey:@"localizedStringKey" value:@"" table:nil]

//图片获取
NSString *bundleImageName=[bundleName  stringByAppendingString:@"myIcon"];

UIImage *tmpImage=[UIImage imageNamed: bundleImageName];

根据上面代码可以找个单例提供bundleName字段,在模块初始化的时候,先判断xxx.bundle有没有,没有的情况bundleName设置成Frameworks/xxx.framework/xxx.bundle,如果没有bundle而是单纯的资源文件,指定到framework目录里就可以随便用了。

只有这样你的pod才算是支持了CocoaPods 动/静态库,也才算是真正的组件化,而且拆包看.app也会感觉优雅很多,分类明确!

CocoaPods 组件化常识普及

模块开发

在模块开发时可以podfile里指定本地路径,.podspec引入工程内但不要加到target里。这样改podspec方便,而且只是修改文件的话可以随时看到pod的真实效果。
上面说的并不是最好的方式,bug fix可能还不错,真开发一大块没有的东西还是先开发最后写podspec为上策!


DingTalk20170818182404.png
  pod 'xxxModule',:path => '../xxxModule.podspec'

指定Spec Repo

在不指定Spec Repo的情况事实是如下面类似写podfile文件,的确也可以更新,但pod内如果有dependency就不行除非你在外部也pod了你dependency的库。。。如此一来你的podfile就比较臃肿看起来,当然的确是可以凑活用的!

  pod 'xxxxx',:git => 'ssh://git@git.xxxx.cn/xxxxx.git',branch:'master'

其实完全可以在podfile最上面写上

source 'ssh://xxx.com/my/Specs.git'

你只需要把官网的Specs弄下来传到你自己的服务器上,至于怎么上传.podspec文件,可以看下面:

#设置一次我们自己的远程仓库源,类似git remote的概念,REPO_NAME仓库源名,SOURCE_URL仓库源远程地址
pod repo add REPO_NAME SOURCE_URL

#上传.podspec文件到自己的本地命名好的那个仓库源
pod repo push REPO_NAME SPEC_NAME.podspec

不过没事的时候要合并一下https://github.com/CocoaPods/Specs,不然你的外部引用可就跟不上时代的潮流了!

弄懂上面那些基本上已经够做好工程管理的组件化了!

相关文章

  • CocoaPods 动/静态库混用封装组件化

    动/静态库混用 pods的动静态库混用,相信大多数人一想到就会头皮发麻,体会过的应该都懂,那种无助感。。。。 问题...

  • ios 制作framework静态库

    新接手的项目要重构,计划往组件化方向搞。提及组件化可能就会涉及到静态库的封装,之前研究过静态库的封装但是那时候没有...

  • 2021-12-17 Podfile 使用和不用use_fram

    cocoapods3种类形的pod组件: 1,如果组件是,.a 静态库,或者.framework 静态库,podf...

  • 代码管理| 创建自己的私有Cocopods库

    前言 iOS组件化的实现基本基于cocoapods,如何使用cocoapods创建自己的组件库,是实现组件化的第一...

  • iOS_组件化开发

    思路:将网络、本地存储等封装成基础组件,将用户、活动等封装成业务组件.然后将这些组件使用cocoapods 私有库...

  • iOS组件化探究之私有库的创建

    iOS组件化实现基本基于cocoapods,如何使用cocoapods创建自己的组件库,是实现组件化的第一要素,下...

  • 组件化学习

    组件化是什么 组件化是通过cocoapods的形式安装各个组件,那么就要了解cocoapods是怎么打造本地私有库...

  • 组件化博客

    iOS组件化组件化-动态库实战Cocoapods整理(三)——编写podspec文件CocoaPods制作第三方代...

  • CocoaPod集成自己的管理库

    组件化之-CocoaPods管理自己的公有库 什么是Cocoapods? 来自官网的解释CocoaPods is ...

  • CocoaPods创建属于自己的Pod库

    项目想要模块化、组件化,就必须了解如何创建CocoaPods库,如何创建CocoaPods库呢,今天我们就来动手开...

网友评论

  • 大良造L:想请教下楼主, 私有pod库引用了另一个私有pod库某个framework的<xxx/xxx.h>文件。 这时候需要写<pod库名/xxx/xxx.h> 才能识别。如:本应该 #import <TencentOpenAPI/QQApiInterface.h> 却报找不到, 用 #import <当前Pod/TencentOpenAPI/TencentOAuth.h> 却可以。 请问是什么原因呢,有解决方案吗?
    heroims:@大良造L 正常如果是为某个framework单独做pod,pod名就是framework名,然后把所有.h按文中放public_header_files字段里。如果是仅pod用到了这个framework就尽量保证可控把想公开让用的放到统一地方<pod库名/.h>方式调用也不用拘泥于怎么调因为本身提供的framework就只是供自己那pod用的,其他pod只是巧了也需要能让对方调到就够了。
    大良造L:1、我理解的意思是,自身的framework只能在自己的pod库环境中找到, 如果其他pod库环境, 是需要加上<pod库/xxx/xxx.h>才能找到这个framework。 2、但是非pod库,如主工程中是能找到的, 这俩者有什么区别吗? 3、 你最后一句话是想告诉我,还是应该加上 <pod库名/>吗? 谢谢楼主的解答。😁
    heroims:其实主要跟framework的打包方式有关系,一般就是私有pod库按文中加public_header_files,可以做成类似<当前Pod/TencentOAuth.h>,核心是找不到framework在哪,要想直接<TencentOpenAPI/QQApiInterface.h>ok,自己需要在podfile里hook加framework路径到工程里也可以或手动加也可以。但从架构角度我pod用的第三方库其实并不希望调用者直接用我内部第三方库而是间接的通过我的pod内部的类使用,并不是直接绕过我用第三方,那样不可控!
  • LamSpeech:请问打包动态库与静态库pod命令以及podspec文件距存在什么区别啊,还是不会打动态库
    heroims:podspec只能指定是静态,默认按podfile指定打,podfile默认是打静态,按文中设置可以打动态
  • subvertWuxu:你好,想问下,比如我现在有三个Pod,A中含有一个静态库,B依赖A,C依赖B。按文档的说法,应该是B需要加static_framework关键字,但我目前的测试结果是,B、C都要加。如果C不加static_framework也会编译失败,这个是为什么呢?
    heroims:@subvertWuxu #7373今天已经把这问题修复merge了,估计下次发版就OK了
    subvertWuxu:@heroims 略坑。。。照这个改法,很多库都要变成static framework了。几十个pod感觉hold不住。。。大神有试过吗?大型工程这么改有什么问题没?
    heroims:@subvertWuxu 这个算bug。。。。#7352上已经提了,说是1.5.0可能修复
  • 93b3a2d4e4e0:厉害厉害!最近准备把友盟从主工程中搬到私有库中,搬过去之后编译报错友盟头文件找不到,真是搞得头皮发麻了。必须给个赞赏支持~
    Marc丶:你好,你们解决了没
  • f9752836b891:大兄弟,昨天 cocoapods 已经更新到 1.4.0.rc.1 版本,但是之前遇到的那个 s.static_framework 的问题我这边还是没有解决,请问你那边能否验证下这个问题?谢谢!
    f9752836b891:大兄弟,你组件中的 xib 文件是放在 source_files 中,还是放在 resource_bundles 中?
    f9752836b891:@heroims 大兄弟,你QQ发我一下,我加一下,顺便把我这边的一个测试工程发给你看下,帮忙指点下,谢了
    heroims:@小小聪明屋123 我这试了是ok的,已经不用手动写头文件路径了
  • 00d8ba13f2db:大兄弟,我还是没明白,例如我现在遇到的问题,就是 只有一个 swift 三方库需要pod,其他的都是OC库,我怎么弄啊,在不使用use framework!的情况下,导入swift的那个三方:flushed:
    heroims:@伏尘 首先你要明白pod只要有swift相关必须用use_frameworks!,剩下的就是看你报了什么错自己慢慢排查了,专错专制
    00d8ba13f2db:求救啊,兄弟:disappointed_relieved:
  • f9752836b891:大兄弟,.podspec内加s.static_framework = true遇到和2楼同样的问题,例如:A是一个私有仓库,B也是一个私有仓库,C是一个包含 .a 的某个第三方 pod 库(UMengUShare/Social/ReducedQQ 友盟分享),现在 B 的 . podspec 文件中依赖 C (B.dependency 'UMengUShare/Social/ReducedQQ'),在 B 中添加 s.static_framework = true 后,是没有问题;但是如果 A 需要依赖 B (A.dependency = B) 编译的时候会报错,这个怎样解决?
    heroims:@小小聪明屋123 你这个是另一个pod的bug,master上已经帮着解决了,下一个预览版应该就修复了,现在你只能手动在build settings里的library search path和head search path加路径即可,另外尽量保持a不直接引用c而是引用b间接引用c会省掉另一堆坑,毕竟release版没放有很多问题正常
    f9752836b891:@heroims 报的是这个错:
    ld: library not found for -lXCShareManager
    clang: error: linker command failed with exit code 1 (use -v to see invocation)
    而且将 s.static_framework = true 去掉后就不会报错,情况和 二楼 的情况应该是一样的
    heroims:@小小聪明屋123 应该是你代码中写的引用头文件方式有问题,这个需要看你怎么写的代码,真正用到大项目里要注意和排查很多地方
  • f9752836b891:我这边在 podspec 中 依赖了 .a 和 .framework 的 pod 库(s.dependency 'UMengUShare/Social/ReducedSina'),当在 podfile 中加了 use_frameworks! 后,pod install 的时候就报错了,按照你你的在 podspec 文件中加了 s.static_framework = true还是不行,请问该怎么解决?谢谢!
    heroims:@小小聪明屋123 1.4.0开始支持的,其次拍平不要用subspec,应该就没问题了,还有如果archive最新预发布版都不行,git上刚修复,你需要自己加一下header search path
    f9752836b891:我这边 cocospod 版本是 1.3.1

    错误信息:

    [!] The 'Pods-XCShareManager_Example' target has transitive dependencies that include static binaries: (/Users/fanxiaocong/Desktop/测试远程私有库/TTT_TTT/功能组件/XCShareManager/Example/Pods/UMengUShare/UShareSDK/UMSocialSDKPlugin/libUMSocialLog.a, /Users/fanxiaocong/Desktop/测试远程私有库/TTT_TTT/功能组件/XCShareManager/Example/Pods/UMengUShare/UShareSDK/UMSocialSDK/UMSocialCore.framework, /Users/fanxiaocong/Desktop/测试远程私有库/TTT_TTT/功能组件/XCShareManager/Example/Pods/UMengUShare/UShareSDK/UMSocialSDK/UMSocialNetwork.framework, /Users/fanxiaocong/Desktop/测试远程私有库/TTT_TTT/功能组件/XCShareManager/Example/Pods/UMengUShare/UShareSDK/SocialLibraries/QQ/libSocialQQ.a, /Users/fanxiaocong/Desktop/测试远程私有库/TTT_TTT/功能组件/XCShareManager/Example/Pods/UMengUShare/UShareSDK/SocialLibraries/Sina/libSocialSina.a, and /Users/fanxiaocong/Desktop/测试远程私有库/TTT_TTT/功能组件/XCShareManager/Example/Pods/UMengUShare/UShareSDK/SocialLibraries/WeChat/libSocialWeChat.a)

    podspec 文件

    s.static_framework = true

    s.subspec 'UMengShare' do |umeng|
    umeng.source_files = 'XCShareManager/Classes/UMengShare/*.{h,m}'

    # 集成新浪微博(精简版1M)
    umeng.dependency 'UMengUShare/Social/ReducedSina'
    # 集成微信(精简版0.2M)
    umeng.dependency 'UMengUShare/Social/ReducedWeChat'
    # 集成QQ/QZone/TIM(精简版0.5M)
    umeng.dependency 'UMengUShare/Social/ReducedQQ'
    end
    heroims:@小小聪明屋123 报的什么错,cocospod版本升级最新,且podspec内不要用subspec,友盟我这试了下没问题
  • JDCaptain:兄弟有个问题请教下,A是主工程,B是一个私有仓库,如果B依赖了静态库,比如B依赖了openssl的静态库,那么A依赖B没问题。如果C也是个私有仓库,C依赖B也没问题。可是A依赖C就不行了,执行文件链接不进来。


    如果可以,你可以试试OpenSSL-Universal,这个是别人对openssl的管理,建立一个主工程去依赖OpenSSL-Universal没问题,可是你若再用一个私有仓库去依赖OpenSSL-Universal,然后主工程再去依赖你建立的私有仓库就不行了。
    f9752836b891:请问你的问题解决没?我这边也遇到了同样的问题
    heroims:@n_f1b8 我试了下正常情况主工程pod没问题,加了use_frameworks!后,把依赖OpenSSL-Universal的私有库的.podspec内加s.static_framework = true也是没问题的,你可以留下邮箱发你demo

本文标题:CocoaPods 动/静态库混用封装组件化

本文链接:https://www.haomeiwen.com/subject/ujxllxtx.html