Showing Posts From
Cocoapods
CocoaPods对三方库的管理探究
- 15 Nov, 2020
CocoaPods是iOS开发中经常被用到的第三方库管理工具,我们有必要深入了解一下它对项目产生了什么影响,以及它是如何管理这些库的。使用pod安装三方库 我们新建一个不带测试模块的名为FFDemo的Swift项目,它的目录结构是这样的 ├── FFDemo │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── Base.lproj │ ├── Info.plist │ ├── SceneDelegate.swift │ └── ViewController.swift └── FFDemo.xcodeproj ├── project.pbxproj ├── project.xcworkspace └── xcuserdata然后我们执行pod init创建一个Podfile模板,在里面引入这两个三方库: target 'FFDemo' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! # Pods for FFDemo pod 'MJRefresh', '~> 3.5.0' pod 'Moya'end成功执行pod install之后我们就将这两个库引入到了项目,这时项目目录变成了这样: ├── FFDemo │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── Base.lproj │ ├── Info.plist │ ├── SceneDelegate.swift │ └── ViewController.swift ├── FFDemo.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ └── xcuserdata ├── FFDemo.xcworkspace │ └── contents.xcworkspacedata ├── Podfile ├── Podfile.lock └── Pods ├── Alamofire ├── Headers ├── Local\ Podspecs ├── MJRefresh ├── Manifest.lock ├── Moya ├── Pods.xcodeproj └── Target\ Support\ Files从目录看,除了pod init引入了Podfile,其余三部分内容:FFDemo.xcworkspace、Podfile.lock、Pods目录都是由pod install之后生成的。我们下面重点讲下这三部分内容。 CocoaPods安装的内容 xcworkspace文件 该文件下包含一个叫contents.xcworkspacedata的文件,它的内容是这样的: <?xml version="1.0" encoding="UTF-8"?> <Workspace version = "1.0"> <FileRef location = "group:FFDemo.xcodeproj"> </FileRef> <FileRef location = "group:Pods/Pods.xcodeproj"> </FileRef> </Workspace>使用xml格式将依赖包含在标签内。 xcworkspace是一个项目容器,当有多个project需要相互依赖时可以用xcworkspace将它们组织起来。pod在首次安装三方库时会生成一个叫Pods.xcodeproj的project管理三方库,然后将该project和主项目的project通过workspace进行关联。这样我们就可以在主工程里引入三方库了,而且三方库由Pods.xcodeproj统一管理,不会对我们原项目产生任何干扰。 Podfile.lock Podfile.lock文件的内容是这样的: PODS: - Alamofire (5.3.0) - MJRefresh (3.5.0) - Moya (14.0.0): - Moya/Core (= 14.0.0) - Moya/Core (14.0.0): - Alamofire (~> 5.0)DEPENDENCIES: - MJRefresh (~> 3.5.0) - MoyaSPEC REPOS: trunk: - Alamofire - MJRefresh - MoyaSPEC CHECKSUMS: Alamofire: 2c792affbdc2f18016e08fdbcacd60aebe1ba593 MJRefresh: 6afc955813966afb08305477dd7a0d9ad5e79a16 Moya: 5b45dacb75adb009f97fde91c204c1e565d31916PODFILE CHECKSUM: 073f3d6d9f03e6a76838ca3719df48ae6cc01450COCOAPODS: 1.9.3因为Podfile文件里可以不指定版本号,而版本信息又很重要,于是就有了Podfile.lock,它里面记录完整的版本信息和依赖关系。它的内容包含以下几大块 PODS PODS是指当前引用库的具体版本号,可以发现我们并没有引入Alamofire,但在PODS里确有它。这是因为Moya中依赖了它,Moya里定义了一个subspec叫Core,这是Moya/Core写法的由来。pod是通过各个库的podspec文件找到对应依赖的,这里可以简单看下Moya的部分podspeec文件内容Moya.podspec: Pod::Spec.new do |s| s.default_subspecs = "Core" s.subspec "Core" do |ss| ss.source_files = "Sources/Moya/", "Sources/Moya/Plugins/" ss.dependency "Alamofire", "~> 5.0" ss.framework = "Foundation" end endDEPENDENCIES DEPENDENCIES为pod库的描述信息,这里内容是同Podfile里的写法。因为我们指定了MJRefresh的版本号,并没有指定Moya的版本号,所以这里内容也是一样的。 SPEC REPOS 这里描述的是仓库信息,即安装了哪些三方库,他们来自于哪个仓库。 trunk是共有仓库的名称,它的地址是https://github.com/CocoaPods/Specs.git,外部使用的三方库大都来自于这里。通常我们还会依赖一些公司内部的私有库,私有库的信息也会显示在这里。 SPEC CHECKSUM 这里描述的是各个三方库的校验和,校验和的算法是对当前安装版本的三方库的podspec文件求SHA1。比如MJRefresh的校验和:6afc955813966afb08305477dd7a0d9ad5e79a16。我们安装的MJRefresh的版本为3.5.0,它在本地的podspec文件路径为:~/.cocoapods/repos/trunk/Specs/0/f/b/MJRefresh/3.5.0/MJRefresh.podspec.json。 这个路径可以通过在安装库时增加 --verbose参数在输出日志里查看。我们对该文件内容通过openssl求sha1摘要: $ pod ipc spec ~/.cocoapods/repos/trunk/Specs/0/f/b/MJRefresh/3.5.0/MJRefresh.podspec.json | openssl sha1 $ 6afc955813966afb08305477dd7a0d9ad5e79a16因为是对podspec.json内容求sha1,所以只要内容发生一点变化,得出的校验和就将大不相同,而这也是校验和设计的目的:podspec文件发生变化意味着版本信息发生了变化,就需要重新同步代码。 大家可能注意到了,我们通常制作私有pod,控制配置信息的文件是podspec格式的,为什么本地文件变成了json格式? 这是因为json格式兼容性更高也更容易批量处理,官方Spec仓库的所有库配置文件都是被转成json格式的。在我们制作私有库的时候是可以直接以podspec的格式推到远程仓库的,但后续解析文件时pod内部检索还是会把它转成json格式。上面的命令是包含了podsepc转json的命令的,转json命令如下: $ pod ipc spec ModuleName.podspecPODFILE CHECKSUM 这个校验和是针对Podfile内容的校验和,如果Podfile内容改变了,该值也会跟着改变。计算方法为: $ openssl sha1 filePath/PodfileCOCOAPODS: 1.9.3 这个代表当前使用的CocoaPod版本号,远程版本管理应该要保证大家使用的pod版本号一致。 Pods Manifest.lock Manifest.lock是Podfile.lock的副本,它是在Pods目录里面。它的作用是这样的,我们通常是不把Pods文件放到版本管理里面,而把Podfile.lock放到版本管理里面。这时对于拉取代码之后是否需要更新pod,就可以通过对比本地的Manifest.lock和远程Podfile.lock是否相同即可。 Targets Support Files Pods安装的依赖是这样的组织形式一个Pods的Project下面有三个Targets,其中三个是安装的依赖库,最后一个Pods-FFDemo是关联三个库的Framework,也即是Pods这个Project的Targets。 Pods-Demo Framework 先看这个Demo的Framework,它会被用于工程项目的引用依赖这个库不会被打进包里,因为Do Not Embed代表并不是包含的关系。 这个工程下的配置文件有这些:许可协议文件 两个以acknowledgements命名的文件是用于管理pod库的许可协议,即三方库必须带有的LICENSE文件,这也是为什么我们在制作pod时会要求我们指定软件协议。 Framework文件 这里还包含了用于管理Module的modulemap和umbrella.h文件。modulemap是对Module的声明文件,制作Framework我们总是需要该文件,它的内容如下: framework module Pods_FFDemo { umbrella header "Pods-FFDemo-umbrella.h" export * module * { export * } }其指向了一个umbrella的头文件,这是制作Framework必须的头文件,modulemap和umbrella.h会在创建Module时自动生成,不建议手动修改其关系。 dummy.m文件 这其实是一个空的.m文件 #import <Foundation/Foundation.h> @interface PodsDummy_Pods_FFDemo : NSObject @end @implementation PodsDummy_Pods_FFDemo @end那为什么要有这个东西呢,包括所有的三方库的包里也会包含一个dummy文件。我在stackoverflow找到了一个解释:Xcode的编译是依赖.m文件的,如果一个库里没有.m文件,将不会被编译,为了防止这种情况就会在每个库里增加一个空的.m文件。 xcconfig文件 xcconfig文件是Build Setting配置项的文件形式,它的优先级大于Xcode内的Build Setting。看一个pod生成的debug模式下的xcconfig文件。 ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/Moya" GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Moya/Moya.framework/Headers" LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" -framework "Foundation" -framework "MJRefresh" -framework "Moya" OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS PODS_BUILD_DIR = ${BUILD_DIR} PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) PODS_PODFILE_DIR_PATH = ${SRCROOT}/. PODS_ROOT = ${SRCROOT}/Pods USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YESxcconfig还有个作用是设置参数,比如我们比较熟悉的PODS_ROOT=${SRCROOT}/PODS,它代表项目根目录下的PODS文件目录。另外两项用于帮助我们在项目中查找三方库的FRAMEWORK_SEARCH_PATHS和HEADER_SEARCH_PATHS也是在该文件内部定义的,这些配置会体现到Build Settings里面:三方库的Framework 各个三方库也都有一些配置文件,他们文件格式基本一致,文件作用跟上面介绍的类似,下图是Moya的配置文件,Xcode中Pods > Pods > Moya > Support Files对应的文件就是该内容。我们可以想一个问题,当安装的第三方库需要依赖于别的库时它是如何去找这个库的呢?Moya是需要使用Alamofire的API的,会有import Alamofire的操作。凭借上面的内容,可以得知Framework的引用是需要在Build Setting里提前该Target,有哪些引用项的。所以这也是Framework里xcconfig文件的作用,可以在Moya的xcconfig文件里找到这个: FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire"而且引用的是跟主项目同一个Alamofire的路径。 Build Phases这里是设置编译阶段配置的地方,当首次pod install成功之后,这里会多几个[CP]开头的配置项(CP即CocoaPods缩写),它们都是由CocoPods添加的脚本内容,执行顺序从上到下。 New System Build 在讲编译脚本之前简单说下New Build System。 New Build System是Xcode10之后苹果推出的新的构建系统,新的构建系统对编译流程的优化做了很多工作,虽然到Xcode12仍兼容旧版的Legacy Build System,但其已经被标记为移除,我们的项目和库都应该使用新版的构建系统进行构建。和新的构建系统随之而来的是在运行脚本时增加的输入输出列表。这是为了控制是否每次编译都需要执行对应脚本,input和output文件可以是单个文件形式,如果文件过多可以放到格式为xcfilelist的文件列表里。 如果没有提供input和output,则每次构建都会运行该脚本。如果提供了,则会在以前从未运行过、某个输入文件被更改或某个输出文件丢失的情况下再次运行。 注意这些是构建脚本的默认逻辑,Xcode还提供了Run Scripts的自定义行为,默认勾选项:Based on dependency analysis,即代表上述逻辑。如果提供了输入输出还需要每次运行,关闭该选项即可。 [CP] Check Pods Manifest.lock 该脚本位于较上方,如果没有Dependencies,开始编译就会执行该脚本,它的内容如下: diff "${PODS_PODFILE_DIR_PATH}/Podfile.lock" "${PODS_ROOT}/Manifest.lock" > /dev/null if [ $? != 0 ] ; then # print error to STDERR echo "error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation." >&2 exit 1 fi # This output is used by Xcode 'outputs' to avoid re-running this script phase. echo "SUCCESS" > "${SCRIPT_OUTPUT_FILE_0}"作用是比较Podfile.lock和Manifest.lock文件是否相同,如果不同就输出错误信息:error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.,并执行退出,这会导致后续项目报错,无法继续编译。 该错误较常见,出现于拉取远端代码,远端pod依赖于本地不一致的情况。这时我们可以根据提示,执行pod install命令,根据Podfile及远端Podfile.lock生成新的Manifest.lock文件。 [CP] Copy Pods Resources 这个一般在以静态库引入的三方库切里面包含资源的话会添加该脚本,其作用是将三方库的资源文件拷贝至项目中。 它的完成是通过运行以下脚本进行的: "${PODS_ROOT}/Target Support Files/Pods-FFDemo/Pods-FFDemo-resources.sh"Pods-FFDemo-resources.sh文件在Pods目录内,该脚本内有个关键函数install_resource: install_resource() { if [[ "$1" = /* ]] ; then RESOURCE_PATH="$1" else RESOURCE_PATH="${PODS_ROOT}/$1" fi if [[ ! -e "$RESOURCE_PATH" ]] ; then cat << EOM error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. EOM exit 1 fi case $RESOURCE_PATH in *.storyboard) ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} ;; *.xib) ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} ;; *.framework) echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" ;; *.xcassets) ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") ;; *) echo "$RESOURCE_PATH" || true echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" ;; esac }删除了一部分日志内容,其内部主要是一个switch语句,根据资源文件的类型进行不同的同步操作。这里重点说下几种重要格式文件的处理方式。 storyboard和xib格式 这两项资源文件是需要编译处理的,利用ibtool命令分别转成sotryboardc和nib格式。 xcassets格式 这里的图片最终会被打包到Assets.car供程序使用,需要使用actool。 Bundle、plist、png等资源 其他类的资源是会走到switch语句最后出口,进行资源路径赋值给$RESOURCES_TO_COPY,在后面的代码中通过rsync命令,将资源同步到构建包的目录。 该脚本会打印很多日志,在使用CocoaPods时如果遇到资源相关的问题都可以遵循错误日志来这里推测定位错误原因。 [CP] Embed Pods Frameworks 该处脚本是直接运行Pods-FFDemo-frameworks.sh。 "${PODS_ROOT}/Target Support Files/Pods-FFDemo/Pods-FFDemo-frameworks.sh"可能你还记得上面说的pod会把多个库的依赖做成一个合并的库,但该库是以依赖的形式引入主工程,但是程序的运行时需要这些库,我们打包时就需要将各个库Embed到项目里,而做这个工作的就是该脚本。 # Copies and strips a vendored framework install_framework() { rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" # other code... # Strip invalid architectures so "fat" simulator / device frameworks work on device if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then strip_invalid_archs "$binary" fi # Resign the code if required by the build settings to avoid unstable apps code_sign_if_enabled "${destination}/$(basename "$1")" }脚本内容主要是调用install_framework函数,将framework内容同步到构建包里。在该函数里还有几个关键方法,strip_invalid_archs用于去除无用架构,code_sign_if_enabled用于framwork签名。
使用Cocoapods管理私有库组件
CocoaPods是OS X和iOS下的一个第三方开源类库管理工具,通过CocoaPods工具我们可以为项目添加依赖库(这些类库必须是CocoaPods本身所支持的),并且可以轻松管理其版本。它是目前iOS开发中使用最广泛的开源库管理工具,如果我们内部协作的组件化能够使用这种方式管理的话,那将是很便利的。 在通过Cocoapods建立内部私有库之前,我们需要再熟悉下Cocoapods的工作流程,我们创建内部私有库时也会依照这个流程来。本文目录 一、Cocoapods的工作流程 二、建立Cocoapods私有库 三、使用私有库 四、问题总结Cocoapods工作流程 工作流程如图所示:远程索引库: 这里存放了各个框架的描述文件,托管在github上: CocoaPods/Specs 本地索引库: 在安装cocoapods时,执行的pod setup就是讲远程索引克隆到本地,本地索引的目录为: ~/.cocoapods/repos/master本地索引和远程索引的目录一致,结构如下:每个库的每个版本都对应一个json格式的描述文件: { "name": "YYImage", "summary": "Image framework for iOS to display/encode/decode animated WebP, APNG, GIF, and more.", "version": "1.0", "license": { "type": "MIT", "file": "LICENSE" }, "authors": { "ibireme": "ibireme@gmail.com" }, "social_media_url": "http://blog.ibireme.com", "homepage": "https://github.com/ibireme/YYImage", "platforms": { "ios": "6.0" }, "source": { "git": "https://github.com/ibireme/YYImage.git", "tag": "1.0" }, "requires_arc": true, "default_subspecs": "Core", "subspecs": [ { "name": "Core", "source_files": "YYImage/*.{h,m}", "public_header_files": "YYImage/*.{h}", "libraries": "z", "frameworks": [ "UIKit", "CoreFoundation", "QuartzCore", "AssetsLibrary", "ImageIO", "Accelerate", "MobileCoreServices" ] }, { "name": "WebP", "dependencies": { "YYImage/Core": [] }, "ios": { "vendored_frameworks": "Vendor/WebP.framework" } } ] }本地索引文件 当执行pod search命令时,如果本地索引不存在,就会创建出来: $ pod search afn Creating search index for spec repo 'master'..本地索引文件路径为: ~/Library/Cache/Cocoapods/Pods远程框架库 以YYImage为例,它的远程框架库就是json文件中的source: https://github.com/ibireme/YYImage.git 所以再用文字总结下Cocoapods工作流程大概就是 1、本地安装cocoapods,建立本地索引库和远程索引库的映射 2、本地项目pod install 3、查找本地索引文件,然后找到各个库对应版本的json文件 4、通过json文件source字段找到引用库的git地址 5、把库文件拉到本地项目 建立Cocoapods私有库(framework) 建议采用framework的形式创建私有库,这可以很好的在开发阶段检查出库的不兼容或者文件权限出现的问题,Swift编写的代码通过Cocoapods生成的都是framework。 0、准备工作: 如何建立远程索引库 首先我们需要建立一个内部的远程索引库,类似Cocoapods/Spec的功能,之后添加的库文件索引文件都会存放到这里:https://zhangferry@bitbucket.org/sealcn/sealrepo.git 建立本地和远程索引仓库的关联: pod repo add SealRepo https://zhangferry@bitbucket.org/sealcn/sealrepo.git执行pod repo可以看到我们有了两个索引仓库,可以去在这个目录~/.cocoapods/repos看到我们刚建立的SealRepo。 如何组织文件结构 我们可以看下Alamofire的文件组织结构:我们看到这几个文件:Source用于存放Framework源文件, Example用于放Demo项目 docs和Documentation放说明文档,这个是可选的, Tests测试文件也是可选。 我们制作私有库时会仿照这个格式。一、制作framework因为是Swift的工程,接口的开放直接通过open、public等关键字指定,所以工程中的ABTest.h头文件可以删除,加入我们自己的库文件。注意:在写公有库文件时,对外界开放的属性,方法需要带上public或者open关键字。 二、添加Example工程 通过Xcode菜单栏File->New->Target...添加一个Example工程。 引入第三方库 如果无第三库引用可以跳过这一步。 注意:引入Podfile文件,需要framework和Example两个target都添加上。 测试项目 需要先编译framework,没有问题之后,导入到Demo项目里 import ABTest运行Dome,测试开发功能有没有问题。 push项目到远程库 如果已经关联过远程私有仓库,这一步可以跳过。 在远程配置一个git地址,然后将本地项目关联到远程私有仓库: git remote add origin 仓库地址如过是首次关联远程仓库,在push之前我们一般需要先拉去远程分支 git pull origin master如果提示: There is no tracking information for the current branch.那是因为本地库和远程库没有建立联系,git认为这两个仓库可能不是同一个,如果我们确认对应库没问题,可以使用: $ git pull origin master --allow-unrelated-histories 将远程库文件强制拉到本地仓库。 之后再执行push命令将项目推到远程仓库。 git push -u origin master三、Cocoapods配置文件 1、添加.swift-version .swift-version文件用来告诉cocoapods当前文件swift的版本,用命令行建立: $ echo "3.0" > .swift-version2、添加LICENSE 每个使用cocoapods添加的库都需要准守开源协议,一般是MIT协议,因为bitbucket没法自动生成,我们可以手动生成这个同名文件,然后把协议内容复制进去: MIT LicenseCopyright (c) [year] [fullname]Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.3、创建库描述文件 可以通过命令行生成描述文件: $ pod spec create ABTest然后我们编辑ABTest.podspec文件,可以仿照下面的写法 Pod::Spec.new do |s|s.name = "ABTest" s.version = "0.0.1" s.summary = "ABTest with Firebase" s.description = "This is a ABTest Framworks on swift" s.homepage = "https://bitbucket.org/sealcn/remoteabtest/src/master/" s.license = { :type => "MIT", :file => "LICENSE" } s.author = { "zhangferry" => "zhangfei@dailyinnovation.biz" }# ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # s.platform = :ios, "8.0" # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # s.source = { :git => "https://zhangferry@bitbucket.org/sealcn/remoteabtest.git", :tag => s.version } s.source_files = "Source", "Source/*.swift"# ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # # s.resource = "icon.png" # s.resources = "Resources/*.png"# ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # s.requires_arc = true s.static_framework = true s.dependency "Firebase/Core" s.dependency "Firebase/RemoteConfig" #s.ios.vendored_frameworks = "ABTest.framework" s.xcconfig = { 'SWIFT_INCLUDE_PATHS' => '$(PODS_ROOT)/Firebase/CoreOnly/Sources' } end此时我们的文件目录看起来应该大概是这个样子:4、验证本地podspec文件 pod lib lint该命令用于检查podspec文件书写是否正确,如果有error需要解决,warning可以不用管(可能会遇到较多问题,需耐心解决0。0)。解决之后再次运行检查命令,当命令行显示: -> ABTest (0.0.1) ABTest passed validation.说明我们本地配置成功了,到这里本地的第一个版本就算完成了! 然后我们需要将本次修改提交打上tag,提交到远程仓库。 git add . git commit -m "build v0.0.1" git push origin master git tag 0.0.1 git push --tags5、验证远程索引文件 上传代码成功之后,我们需要再次验证它跟远程仓库(ABTest远程库和.podspec)是否匹配正确,执行: pod spec lint当出现: ABTest.podspec passed validation时,说明我们远程仓库匹配正确。 6、提交podspec文件到SpecsRepo $ pod repo push SealRepo ABTest.podspec这个命令会包含pod spec lint命令,验证通过之后,会添加.podspec文件到本地索引库:和远程索引库:使用私有库 引用私有库 我们可以像使用其他库文件一样在Podfile文件中添加使用私有库了,引入方法有两种: 1、全局添加 在Podfile文件最上面添加一行: source 'https://zhangferry@bitbucket.org/sealcn/sealrepo.git'注意:如果私有仓库和cocoapods仓库出现同名库,会出现不可预期的情况(随机拉下来公有库或者私有库文件)。这时我们需要使用单独添加的方式。 2、单独添加 pod 'ABTest', :git => 'https://zhangferry@bitbucket.org/sealcn/remoteabtest.git'使用时通过import方法导入库就可以了。 更新私有库 当我们需要升级私有库,添加或者修改方法时,只需要: 1、修改.podspec文件中s.version的版本号 2、提交本地修改至远程,打上对应tag 3、使用项目的工程执行pod update 可能遇到的问题 1、pod search 查不到本地库 这个可能是cocoadpods本身问题,pod install安装没有问题 2、更新了版本,但是pod update没有找到 我们可以采用如下形式,手动指定版本号: pod 'ABTest', :git => 'https://zhangferry@bitbucket.org/sealcn/remoteabtest.git', :tag => '0.0.4'3、提示The 'Pods-App' target has transitive dependencies that include static binaries 这是因为引入的库被编译成了静态库,我们可以在.podspec文件中加入: s.static_framework = true4、引入的第三方库,在pod lint时提示找不到 可以手动指定pod目录,将firsebase替换成你的库文件路径: s.xcconfig = { 'SWIFT_INCLUDE_PATHS' => '$(PODS_ROOT)/Firebase/CoreOnly/Sources' }5、提示source_files对应文件为空 每次pod lint时都是根据版本号进行查找的,可以检查下当前修改跟版本号是否对应。