美文网首页收藏PodSpeccocoapods
CocoaPods 集成私有库

CocoaPods 集成私有库

作者: 取水 | 来源:发表于2017-05-28 08:40 被阅读1532次

    前言

    Private Spec Repo, 我把它叫做私有库。有更好的叫法,请一定告知我,不胜感谢。

    CocoaPods version: 1.2.1.

    什么时候用

    CocoaPods is a great tool not only for adding open source code to your project, but also for sharing components across projects. You can use a private Spec Repo to do this.

    官方说法,想要要在不同的项目中分享组件,这时你该创建私有库了。

    怎么做

    官方文档写的真是简洁,本以为实际操作也会如此! 是我太年轻了。

    按照文档上说就三步:

    • Create a Private Spec Repo
    • Add your Private Repo to your CocoaPods installation
    • Add your Podspec to your repo

    创建一个 repo, 添加 repo 到你的 CocoaPods 里, 然后推送 podspec 到 repo 里。

    当然,这只是概述。会有各种细节在等着你,让你无所事事,不得寸进。

    会在下面来说下最近一段时间我的历程和爬过的坑。

    私有库和公有库的异同

    Podfile 里继承 AFNetworking: pod 'AFNetworking'

    当我们执行 pod install 后,CocoaPods 做了什么?

    CocoaPods 会去 ~/.cocoapods/repos/master/Specs/ 目录下寻找是否有 AFNetworking 的 podspec?如果没有,则退出,并给出提示无法找到;如果有,则按照 podspec 中记录的第三方库的 GitHub 地址去下载并继承到工程里。

    其中,master/Specs 相当于一个索引文件,里面记录的都是各个第三方库的url,然后当我们需要安装的时候,再去通过这些 url 去下载具体的第三方库。

    而 master/Specs 是一个 CocoaPods 的 github repo,专门用来负责存储第三方库 url 的索引文件。

    我们平时发布的库的操作有2个步骤。一个是创建库并封装功能上去,然后将封装好的库的url告知CocoaPods。这样,其他人就能获得我们发布的三方库了。

    所以,我们明晰了想要一个第三方库生效的2个条件:

    • 必定有一个 repo 存储在服务器上,他人可以获得.
    • 有一个中心化的 Specs repo 用来存储第三方库的索引文件 podspec.

    针对上面,公有库和私有库的区别就很明显了。

    Item 公有库 私有库
    库 repo github - 公开项目 随意(想放哪就放哪)
    索引中心 Specs github - CocoaPods/Specs Specs (想放哪就放哪)

    私库库 repo 的位置不定,可以在任何位置,只要能够索引到就行。有的时候也可以不是 repo,而是可下载的文件。私有/Specs 代替 CocoaPods/Specs 用来作为私有库的中心.

    创建私有库具体过程

    下面将要交大家如何一步步去创建私有库。已 gitlab 为事例服务器。当然你也可以在别的git服务器上创建,如:coding.net,或者公司内部的git服务器。

    1.gitlab 上创建 lib repo 和 Specs repo (Specs 是用来管理私有 lib repo 的)

    私有的 lib repo :

    cocoapodsDemo1.png

    Specs repo :

    specs repo

    这里已经有内容了,其实就是建立一个名字为 Specs 的空 repo.

    2.创建 pod lib repo

    $ pod lib create PrivateLibraryRepo
    

    添加功能实现文件

    在 PrivateLibraryRepo/Classes 下添加功能类 Foo. 目录结构如下:

    ├── PrivateLibraryRepo
    │   ├── Assets
    │   └── Classes
    │       ├── Foo.h
    │       ├── Foo.m
    │       └── ReplaceMe.m
    

    进入到 Example 木下,执行:pod install 安装刚刚我们添加的帮助类。

    Foo 内容如下:

    #import "Foo.h"
    
    @implementation Foo
    - (void)foo { NSLog(@"Foo say hello!"); }
    @end
    

    在 PrivateLibraryRepo.xcworkspace 的 PZViewController 里,引入Foo并调用。

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        Foo *f = [Foo new];
        [f foo];
    }
    

    输出结果:Foo say hello!

    说明Foo库函数没有问题。可以进行下一步了。

    **修改 podspec **

    找到 PrivateLibraryRepo.podspec 并修改。podspec 语法参见 Podspec Syntax Reference.

    因为是私有库,所以我们要指定私有库的url地址,有可能在任何地方。然后,可能根据个人心意去修改版本号。有的时候还要修改源文件目录或者添加资源文件夹等等。根据个人情况更改。

    下面是改好的,有备注。

    Pod::Spec.new do |s|
      s.name             = 'PrivateLibraryRepo'
      #版本号,默认从0.1.0开始
      s.version          = '0.1.0'
      s.summary          = 'A short description of PrivateLibraryRepo.'
    
      s.description      = <<-DESC
    TODO: Add long description of the pod here.
                           DESC
      s.homepage         = 'https://gitlab.com/zlanchun/PrivateLibraryRepo'
      s.license          = { :type => 'MIT', :file => 'LICENSE' }
      s.author           = { 'feimengchang' => 'zlanchun@gmail.com' }
      #source 地址,当前私有库的git地址                                              
      s.source           = { 
                            :git => 'https://zlanchun@gitlab.com/zlanchun/PrivateLibraryRepo.git', 
                            :tag => s.version.to_s 
                           }
    
      s.ios.deployment_target = '7.0'
      s.platform     = :ios, '7.0'
      s.source_files = 'PrivateLibraryRepo/Classes/**/*'
    end
    

    3.验证 pod lib repo

    lib repo 验证

    在目录 PrivateLibraryRepo 根下执行命令。

    $ pod lib lint --allow-warnings
    

    --allow-warnings 的意思是忽略所有警告,并通过校验。

    pod lib lint 用于检测本地 lib repo 的有效性。如果无效,则会有报错 error 出现。有过有效,就会给出提示信息,如:PrivateLibraryRepo passed validation.

    spec 验证

    索引文件 podspec 验证。注意这个时候,因为我们的库文件都在本地,而podspec 验证是需要去和服务器通信的,所以此步骤会出错是正常的。

    $ pod spec lint 
    

    报错:

    fatal: Remote branch 0.1.0 not found in upstream origin

    留待我们后面解决。

    4.上传 lib repo 到服务器

    $ git remote add origin git@gitlab.com:zlanchun/PrivateLibraryRepo.git
    $ git add .
    $ git commit -m "Initial commit"
    $ git push -u origin master
    //tag 值要和podspec中的version一致
    $ git tag 0.1.0
    //推送tag到服务器上
    $ git push --tags
    

    这里除了tag 指定版本外,还可以新建一个分支,分支名字和version值一致,同样能起到作用。如果什么都没有,则会报错。报错也是这样的:

    fatal: Remote branch 0.1.0 not found in upstream origin

    5.podspec 再次验证

    $ pod spec lint --allow-warnings

    这一次会验证通过:pod spec lint --allow-warnings。

    6.发布 podspec

    指定管理 lib repo 的 Specs repo 的 url

    $ pod repo add NAME URL [BRANCH]
    

    如:

    $ pod repo add PrivateLibraryRepo https://zlanchun@gitlab.com/zlanchun/Specs.git
    

    推送 podspec 到 Specs repo

    $ pod repo push REPO [NAME.podspec] --sources=xxx,yyy
    

    注意:

    --sources 用来指定 Specs 的 repo 地址。默认是推送的github上。因为这里我们是私库,用的是gitlab,所以需要设定这个值。

    如:

    $ pod repo push PrivateLibraryRepo PrivateLibraryRepo.podspec --sources=https://zlanchun@gitlab.com/zlanchun/Specs.git --allow-warnings
    

    验证

    新建一个工程,在其Podfile 里,加入如下内容:

    
    source 'https://zlanchun@gitlab.com/zlanchun/Specs.git'
    source 'https://github.com/CocoaPods/Specs.git'
    
    target 'Test' do
      # Uncomment the next line if you're using Swift or would like to use dynamic frameworks
      # use_frameworks!
    pod 'PrivateLibraryRepo'
    
    end
    
    

    然后,执行 pod install:

    cocoapodsDemo2.png

    注意:

    Podfile 里头部的2个source。pod 会按照上下顺序去搜索,如果搜索到lib则进行下一步动作,如果没找到,则切换到下一个source url继续搜索。

    这里我们指定先搜索我们自己的 Specs repo,然后再去搜索 CocoaPods的 Specs.

    私有库依赖私有库

    当我们有2个私有库,其中一个依赖另一个的时候,就会有各种问题出来阻挠你,说多了都是泪。

    Podspec 语法:dependency

    podspec 的 dependency 用来指定依赖关系。

    eg: spec.dependency 'AFNetworking', '~> 1.0'

    ~> 1.0.1 is equivalent to >= 1.0.1 combined with < 1.1. Similarly, ~> 1.0 will match 1.0, 1.0.1, 1.1, but will not upgrade to 2.0.

    Command-line 语法:pod lib lint --sources

    The sources from which to pull dependent pods (defaults to https://github.com/CocoaPods/Specs.git). Multiple sources must be comma-delimited..

    sources 用于指定 pods 地址,默认指向 master ,如果有多个源用逗号分隔。下面会用到。

    具体实现

    我们新建一个私库 PrivateLibraryDependency:

    $ pod lib create PrivateLibraryDependency
    

    PrivateLibraryDependency 依赖2个库,一个私库PrivateLibraryRepo,一个SDWebImage。

    更改 PrivateLibraryDependency.podspec

    • 修改 repo 地址
    • 添加依赖
      • s.dependency 'SDWebImage', '~> 4.0.0'
      • s.dependency 'PrivateLibraryRepo', '~> 0.1.0'

    在 Example 目录下的 Podfile 添加source ,然后执行 pod install。

    #Podfile
    source 'https://zlanchun@gitlab.com/zlanchun/Specs.git'
    source 'https://github.com/CocoaPods/Specs.git'
    
    cocoapodsDemo3.png

    根目录下验证lib,这里要添加--sources指定私库Specs地址,因为我们的依赖里有私库,所以要指定。否则会报错。

    $ pod lib lint --sources=https://zlanchun@gitlab.com/zlanchun/Specs.git,https://github.com/CocoaPods/Specs.git
    

    上传库到服务器,打tag。然后验证spec,同样需要指定--sources

    $ pod spec lint --sources=https://zlanchun@gitlab.com/zlanchun/Specs.git,https://github.com/CocoaPods/Specs.git
    

    发布到私Specs repo里,注意这里的source值是 private,master url.因为,我们依赖的既有私有库也有公有库。

    $ pod repo add PrivateLibraryDependency https://zlanchun@gitlab.com/zlanchun/Specs.git
    $ pod repo push PrivateLibraryDependency PrivateLibraryDependency.podspec --sources=https://zlanchun@gitlab.com/zlanchun/Specs.git,https://github.com/CocoaPods/Specs.git
    
    cocoapodsDemo4.png

    subspec 依赖 subspec

    subspec 用来指定子模块的。

    在 PrivateLibraryDependency 目录下新建 ModuleA 和 ModuleB 目录。然后创建 ModuleA.{h,m} 和 ModuleB.{h,m}。

    目录结构:

    ├── PrivateLibraryDependency
    │   ├── Assets
    │   ├── Classes
    │   │   └── ReplaceMe.m
    │   ├── ModuleA
    │   │   ├── ModuleB.h
    │   │   └── ModuleB.m
    │   └── ModuleB
    │       ├── ModuleB.h
    │       └── ModuleB.m
    

    这里,我们假定 子模块 ModuleA 依赖 ModuleB , podspec 如下:

    
    s.subspec 'ModuleA' do |a|
      a.source_files = 'PrivateLibraryDependency/ModuleA/**/*.{h,m}'
      a.dependency 'PrivateLibraryDependency/ModuleB'
    end
    
    s.subspec 'ModuleB' do |b|
      b.source_files = 'PrivateLibraryDependency/ModuleB/**/*.{h,m}'
    end
    

    注意:子模块依赖的时候,依赖里一定要加上当前库前缀。如:a.dependency 'PrivateLibraryDependency/ModuleB'

    参考 PrivateLibraryDependency 我已经把 ModuleA 和 ModuleB 加进去了。

    集成静态库和动态库(.a 和 framework)

    集成 framework 到工程里

    集成第三方的 framework (vendored_frameworks):

    framework 文件要单独集成到一个repo里,同时这个repo里不能包含别的代码文件。

    这里以支付宝SDK为例,提取支付宝SDK的 AlipaySDK.bundle 和 AlipaySDK.framework 放到单独的 repo 里。

    podspec 相应内容如下:

    s.resources = "**/*.bundle"
    s.vendored_frameworks = "**/*.framework"
    

    整个 repo 目录结构:

    .
    ├── AlipaySDK.bundle
    ├── AlipaySDK.framework
    ├── AlipaySDKIniOS.podspec
    ├── LICENSE
    └── README.md
    

    集成系统自带的framework (frameworks / libraries):

    
    # 系统的
    s.frameworks = 'SystemConfiguration', 'CoreTelephony', 'QuartzCore', 'CoreText'  , 'CoreGraphics', 'UIKit', 'Foundation', 'CFNetwork', 'CoreMotion'
    s.libraries = 'c++', 'z'
    

    注意:libc++.tbd 集成的时候,要去掉 lib 前缀。

    参考这里 AlipaySDKIniOS

    集成 .a 库到工程里

    .a 集成的时候,稍微麻烦些!需要同时修改 podspec 和 Podfile 文件。

    以集成 openssl 为例,podspec 中需要更改如下内容:

    s.subspec 'OpenSSL' do |openssl|
      openssl.source_files = 'AlipayWrapper/Openssl/**/*.h'
      openssl.public_header_files = 'AlipayWrapper/Openssl/**/*.h'
      openssl.ios.preserve_paths      = 'AlipayWrapper/StaticLibrary/libcrypto.a', 'AlipayWrapper/StaticLibrary/libssl.a'
      //指定 .a 文件为 vendored_libraries
      openssl.ios.vendored_libraries  = 'AlipayWrapper/StaticLibrary/libcrypto.a', 'AlipayWrapper/StaticLibrary/libssl.a'
      //需要指定为库名字
      openssl.libraries = 'ssl', 'crypto'
      //需要配置 HEADER_SEARCH_PATHS
      openssl.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/#{s.name}/AlipayWrapper/Openssl/**" }
    end
    

    Podfile 里需要添加如下内容,否则会报错:

    pre_install do |installer|
      # workaround for https://github.com/CocoaPods/CocoaPods/issues/3289
      def installer.verify_no_static_framework_transitive_dependencies; end
    end
    

    参考AlipayWrapper,我把 AlipaySDK Demo 的帮助类集成到 pods 了,这样就省去了直接导入工程添加依赖库的步骤了。

    各种错误集合

    Generated duplicate UUIDs

    [!] [Xcodeproj] Generated duplicate UUIDs:
    
    PBXBuildFile -- /targets/buildConfigurationList:buildConfigurations:baseConfigurationReference:|,buildSettings:|,display
    

    解决办法:

    这时一个无害的报错,可以在 Podfile 头部加入如下内容修复:

    install! 'cocoapods', :deterministic_uuids => false
    

    2. <PBXResourcesBuildPhase UUID=2E1007321C278F4300BCFC15>` attempted to initialize an object with an unknown UUID

    [!] `<PBXResourcesBuildPhase UUID=`2E1007321C278F4300BCFC15`>` attempted to initialize an object with an unknown UUID. `70CED53B1EC996BB008DF9D5` for attribute: `files`. This can be the result of a merge and  the unknown UUID is being discarded.
    

    解决办法:

    General -> Linked Frameworks and Libraries 删除 libPods-xxx.a

    然后,重新执行 pod install

    3.Unable to find a specification for depended upon by

    [!] Unable to find a specification for `TESTUtilitis (~> 1.0.0)` depended upon by `TESTVendors`
    

    这个问题是更新库之后,没有更新本地的 repo,所以执行:

    $ pod repo update TESTUtilitis
    

    4.target has transitive dependencies that include static binaries

    [!] The 'Pods-AlipayWrapper_Example' target has transitive dependencies that include static binaries: (/Users/z/Desktop/github/Alipay/AlipaySDK/AlipayWrapper/Example/Pods/AlipaySDKIniOS/AlipaySDK.framework)
    

    这个问题是当我们要用 cocoapods 集成 xxx.a 文件的时候出现的。需要改2个地方:podspec 和 Podfile.

    podspec

     s.dependency 'AlipaySDKIniOS', '~> 15.2.0'
    //添加 target_xconfig
      s.pod_target_xcconfig = {
        'FRAMEWORK_SEARCH_PATHS' => '$(inherited) $(PODS_ROOT)/AlipaySDKIniOS',
        'OTHER_LDFLAGS'          => '$(inherited) -undefined dynamic_lookup'
      }
    

    Podfile

    #在最后面添加如下内容:
    pre_install do |installer|
        # workaround for https://github.com/CocoaPods/CocoaPods/issues/3289
        def installer.verify_no_static_framework_transitive_dependencies; end
    end
    

    详情看这里:https://github.com/CocoaPods/CocoaPods/issues/3289

    5. Use the $(inherited) flag

    [!] The `TEST [Debug]` target overrides the `FRAMEWORK_SEARCH_PATHS` build setting defined in `Pods/Target Support Files/Pods-TEST/Pods-TEST.debug.xcconfig'. This can lead to problems with the CocoaPods installation
        - Use the `$(inherited)` flag, or
        - Remove the build settings from the target.
    

    解决:

    Go into the Build Settings for "Framework Search Paths" and change the value for your target to be "$(inherited)"

    参考

    6. [!] Unable to find a pod with name, author, summary, or description matching AlipaySDKIniOS

    问题:trunk 成功后,无法搜索到。同时已经 pod repo udpate 到最新了,还是无法搜索到。

    原因是:

    ~/Library/Caches/CocoaPods/search_index.json pod setup生成的索引文件没有更新。

    解决:

    删除后,从新 search 就好了。

    相关文章

      网友评论

      • 呵呵哈哈嘿嘿:你好,我的私有库都是放在SVN上,A库依赖B库,写好A的podspec文件后,验证,老是不通过,报错Encountered an unknown error (Unable to find a specification for `xxx` depended upon by `xxx`),请问怎么解决?
      • Ali2016:pre_install do |installer|
        # workaround for https://github.com/CocoaPods/CocoaPods/issues/3289
        def installer.verify_no_static_framework_transitive_dependencies; end
        end

        楼主,podfile 加上这句 打开use framework 也是报错
        取水:@Ali2016 :joy: 我把百度地图直接拖到工程了
        Ali2016:另外还有个问题是libcrypto.a,libssl.a 如果两个库都有用到,cocoapod install的时候会报重复的错误,例如AlipayWrapper 引用了那两个.a文件,百度地图也引用。怎么解决?
      • 神魔狼:楼主 写的好多啊 厉害:+1:
        想问下 上传到pod的项目可以是framework吗,我想pod “xxx” 下来的是framework或者静态库可以吗?
        神魔狼:@取水 还有个问题 我集成的framework中 还包含我自己的一个lib.a 然后我使用s.ios.vendored_libraries = 'XXXFramework/Assets/lib.a' 打包完成后的framework里面并没有这个.a 我把弄好的framework集成到Demo项目中后还要从新加入lib.a才可以 好无语啊

        能不能把.a也集成到framework里面去啊:sleepy:
        神魔狼:@取水 感谢楼主 我搞定了:smiley:
        取水:@神魔狼 可以的。.a 和 framework 的都可以集成的。可以看看这个:https://github.com/EvoIos/AlipaySDKIniOS
      • WhyNotMel:您好 我在设置了podsepc中的依赖关系之后,demo更新了pod之后,依赖的私有库并没有出现在文件夹,也没办法使用,请问知道问题在哪吗……
      • bullet_wu:target has transitive dependencies that include static binaries
        s.dependency 'AlipaySDKIniOS', '~> 15.2.0'
        //添加 target_xconfig
        s.pod_target_xcconfig = {
        'FRAMEWORK_SEARCH_PATHS' => '$(inherited) $(PODS_ROOT)/AlipaySDKIniOS',
        'OTHER_LDFLAGS' => '$(inherited) -undefined dynamic_lookup'
        }
        中的 OTHER_LDFLAGS 为什么要设置 我不设置也一样通过呀?
      • 世界的一缕曙光:看了你的文章, 踩了一些坑(其实是自己的问题)。感觉讲解的顺序不太合理。应该先创建只包含spec的repo,然后再开始你下面的流程。
        刚开始看你的文章时,我看见那个https://zlanchun@GitLab.com/zlanchun/Specs.git中的spec很懵逼,都不知道这个是从哪里来的。在网上看了一个视频才知道。
        其他的都很好!!!
        取水:@Hello_Dear a.source_files = 'PrivateLibraryDependency/ModuleA/**/*.{h,m}' 这个路径是你自己定义的啊,想放到哪都行,前提是你能找到那个路径
        世界的一缕曙光:@取水 这次真是被你坑了一把,希望以后能把文章再好好校验之后再发布。:sweat:
        subspec 依赖 subspec中 a.source_files = 'PrivateLibraryDependency/ModuleA/**/*.{h,m}' 的ModuleA路径应该是xxx/Classes/ModuleA。
        取水:zlanchun/Specs 是一个私有的用来管理自己私有库的中心话 repo,也是自己创建的。我把内容改了改。

      本文标题:CocoaPods 集成私有库

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