通过CocoaPods分发框架的10个技巧

在我相对较短的iOS开发生涯中,我与CocoaPods建立了爱恨交加的关系。 当它起作用时,它很棒,但是使它按您需要或期望的方式工作通常具有挑战性。 但是,在我最近的项目中,我对它有一个健康的尊重,我希望这些技巧可以帮助您充分利用真正的,社区开发的依赖项管理器,尽管有一些小缺点。

以下技巧是我主要是通过反复试验获得的小智慧。 CocoaPods文档简洁明了且有用,但是对于初学者来说有时不够详尽,在某些情况下,有用的功能和选项是非常隐蔽的。

毋庸置疑,所有观点都是我的观点,尽管我相信所写内容都是准确的,但我对此不做任何保证。 在这种情况下,我请您参考标准的开发者免责声明:

“它可以在我的机器上工作!”

在Flickr上浏览Mike Rohde的照片。 Mike Rohde已将5118张照片上传到Flickr。

www.flickr.com

当我刚开始使用CocoaPods时,在发布之前测试我的Podspec更改似乎是一门黑手艺。 有关如何执行此操作的指令很多,但是对于初学者来说,没有一个指令特别简单。 大多数说明都谈论如何设置自己的本地规范存储库,并将Podspec推送到其中,然后在Podfile中更改source参数以指向本地本地存储库。

我发现此工作流程缓慢且麻烦,但是当时,我不知道有更好的方法,直到我发现了一个非常简单的窍门:在本地更改Podfile,然后在测试目标中,指定依赖项时,只需在行尾添加, :path => '/path/to/your/spec.podspec' to , :path => '/path/to/your/spec.podspec' 。 这将告诉CocoaPods在您指定的位置而不是远程规格存储库中查找Podspec。

  #示例Podfile 
#platform:ios,'9.0'target'MyApp'做
#如果不想使用动态框架,请注释下一行
use_frameworks!#MyApp的广告连播
#:path应该表示相对于包含Podfile的目录的路径,该目录包含Podspecpod“ MyPod / Core”,:path =>'../../mypodspec.podspec'
结束

通常,Podspec会指向GitHub上的特定发行版标签,该标签将告诉CocoaPods查找该特定发行版,下载代码并进行构建。 但是,如果您还没有准备发布库,并且没有在远程仓库上创建发布标签,该怎么办? 不要惊慌:这是一个妥善保管的秘密,但是CocoaPods也可以直接从特定分支提取代码,从而无需在GitHub上发布标记版本。 在构建时确实会收到警告,说明您尚未指定发布标签,但除此之外,它的工作还不错,并且CocoaPods从指定的分支中提取了最新的提交。 在文档中确实对此做了简短的提及,但是没有给出显示确切语法的示例。 在您的Podspec中,通常会指定

  s.source = {:git =>“ https://github.com/your-company/your-repo.git”,:tag =>“#{s.version}”} 

…您可以改用

  s.source = {:git =>“ https://github.com/your-company/your-repo.git”,:branch =>“您的分支” } 

您的Podspec现在将从您指定的分支中拉出。 切记在发布Podspec之前将其改回,除非您确实希望Pod的用户从特定分支中拉出!

有时,第三方的依赖是不可避免的。 有时,这种依赖关系实际上可能是您自己编写的,并且恰好发布在单独的pod中。 幸运的是,CocoaPods使这一过程变得非常简单,您甚至可以为所支持的每个平台指定单独的依赖项。 请参阅此处的语法。 用户安装您的pod时,依赖关系将自动下载并生成。

  #注意:依赖关系不需要2个运算符之间的等号 
#依赖关系可以是CocoaPods上可用的现有容器,也可以是同一文件中的现有子规范。ios.dependency“ MyDependency”#将通过CocoaPods拉出“ MyDependency”

在我看来,子规格是CocoaPods的最佳功能之一。 有时,您想让框架用户可以选择要安装库的哪些组件。 使用Subspec,您可以轻松地将代码拆分为单独的模块,并且用户只需安装所需的位。 或者,如果要让他们选择将整个框架作为一个捆绑包安装,则可以指定引用整个项目的默认Subspec。

  Pod :: Spec.new do | s | 
s.name =“ ExampleSpec”
s.version =“ 1.0.0”
#...
s.default_subspec =“完成” s.subspec“完成”做|完成|
complete.source_files =“ myproject / ** / *”
#(可选)排除特定平台的文件
complete.osx.exclude_files =“ myproject / ios / *”
ends.subspec“ Core”做| core |
core.source_files =“ myproject / core / *”
ends.subspec“ MyOptionalModule”做|可选|
optional.source_files =“ myproject / optional / *”
#MyOptionalModule取决于“ Core” ^^
optional.dependency“核心”
end#(可选),指定整个Pod的依赖项:
s.ios.dependency“ MyThirdPartyPod”结束

有时,我们需要编写仅对CocoaPods构建有效的代码,或者相反,当通过CocoaPods构建框架时,会忽略这些代码。 例如,将我们的代码分成模块(单独的Xcode目标),并且在一个或多个模块之间存在依赖关系; 如果一个模块在其同级模块之一中引用代码,则需要导入该模块(例如,在Swift中import MyModule ),但是由于CocoaPods为所有子规格生成了一个单一的产品(框架),因此CocoaPods不需要这样做,并且会导致编译错误。

幸运的是,CocoaPods提供了一个漂亮的“活动编译条件”,它使我们能够编写仅在使用CocoaPods时才进行编译的代码。 截至2018年11月,我在CocoaPods的文档中找不到对此的任何引用,但也许我错过了一些东西。 想一想,我什至不记得我是如何首先找到这些信息的。 要使用此功能,可以使用以下语法包装所需的代码:

  //如果不使用CocoaPods,则仅导入MyModule(例如,迦太基) 
#if!COCOAPODS
导入MyModule
#endif //仅在COCOAPODS编译条件处于活动状态时设置myString
var myString =“” #if COCOAPODS
myString =“ CocoaPods”
#万一
print(myString)//打印CocoaPods(如果框架由CocoaPods构建)

我发现在同一项目中支持CocoaPods和Carthage时,此功能非常有用,因为所需的import样式不同。

有时,我注意到在安装新的Pod并运行项目之后,即使Pod已成功安装,并且我可以在项目中看到代码,但在应用程序运行时不会执行新代码。 通常,这是由于Xcode无法在工作区中正确检测到更改,因此您最终获得了一个缓存的内部版本。 清洁构建文件夹通常可以立即解决此问题(产品>清洁构建文件夹)。

最近,我还看到一个问题,我无法在Pod中包含的代码中设置断点。 在使用了一些Xcode项目设置(调试信息格式,符号剥离),清理构建文件夹并重新编译之后,此问题消失了。 随后,我无法在一个新项目中重现该问题,但我想提一下这个问题,以防您遇到类似的情况。 Xcode有时是一种奇怪的野兽,有时候,打开和关闭项目设置可以带来很多好处!

一直想知道那use_frameworks!是什么use_frameworks! 行在您的Podfile中执行? 那是在告诉CocoaPods建立一个动态框架,而不是一个静态库。 在Xcode 9之前,这是基于Swift的框架的唯一选择,因此CocoaPods需要use_frameworks! 指令(如果您正在构建Swift框架)。 从CocoaPods 1.5.0开始,Swift框架也可以编译为静态库。 对于具有大量动态框架的应用程序,您可能会发现通过将静态库转换为静态库来改善启动时间 您的依赖项。 只需注释掉use_frameworks! Podfile中的一行会导致CocoaPods将Pod编译成静态库,而不是动态框架。 由于切换起来非常容易,因此您可以轻松地尝试两者,并查看哪种方法最适合您的应用。

准备发布Pod时,需要在CocoaPods Trunk中注册。 Trunk是一项服务,可让您在将Podspec推送到主规格存储库之前先向CocoaPods进行身份验证。 无需使用通常的用户名和密码身份验证,而是使用电子邮件地址在计算机上创建会话。 当您注册新会话时,Trunk将向您发送电子邮件以确认您的身份,然后该会话开始。 您只能为自己的软件包推送新的Podspec。 如果您需要成为包装上的合作者,则所有者必须使用pod trunk add-owner命令添加您。 发布广告连播后,您将在cocoapods.org索引上获得一个清单,并且您的软件包将可用于常规安装。

默认情况下,CocoaPods将使您的所有源文件都可以在框架中用作公共头文件。 这对于希望以后调试您的框架的人来说是一件好事,但是有时候,您可能希望仅将其限制为某些文件,或者确实删除所有公共标头。 要仅添加特定文件,可以使用public_header_filesprivate_header_files指令指定要包含在公共头文件中的自定义文件模式。 如果您希望所有文件都包含在公共头文件中,则只需从Podspec中完全省略这些行即可。

作为框架供应商,您希望最终用户能够尽可能轻松地安装框架,无论他们使用什么工具集。 令人遗憾的是,在Apple世界中,没有支持所有平台(iOS,tvOS,macOS,watchOS,Linux)的官方软件包管理器。 Swift项目正在Swift Package Manager上运行,但是当前仅支持macOS和服务器端Swift(在Linux上运行),这使其不适用于iOS / watchOS / tvOS框架。 CocoaPods是解决此问题的一种方法,总的来说,它做得很好,但它仍然是第三方产品,如果有一个官方维护的跨平台工具,那就更好了。

另一个常用(且越来越受欢迎)的依赖性管理器是Carthage。 迦太基与CocoaPods不同,因为它是完全分散的。 它不依赖于包的中央索引。 相反,它通过查看托管在GitHub上的Xcode项目并构建在该项目中找到的所有“共享”方案来工作。 这些方案都将构建到单独的.framework文件中。 作为框架供应商,支持多个软件包管理器是一个额外的负担,但是如果要支持尽可能广泛的用户群,则需要这样做。 每个依赖项管理器都有其自身的缺点。 对于迦太基来说,我要说的最大缺点是缺乏灵活性。 例如,无法在Cartfile中指定要构建的特定方案,这意味着即使您只需要一个方案中包含的模块,也必须构建整个项目。 另一方面,CocoaPods更加灵活,但是一些开发人员不喜欢将新工作区引入项目的方式。 总而言之,在两者都使用过的情况下,相对于相对简单的Carthage,我倾向于CocoaPods的灵活性,但您可能有不同的看法。


我希望您发现这篇文章内容丰富且有用。 如果没有其他问题,下次我在Pod开发期间遇到问题时,它将用作个人检查清单! 这是我的第一篇技术博客文章,非常感谢您提供任何反馈。