我如何建立我的第一个开源库

上周,我发布了我的第一个开源库QuickTicker。 这是一个Swift库,可让您使用一行代码创建简单的股票行情动画。 结果看起来像这样:

在这篇文章中,我想谈谈这个项目并介绍:

  • 为什么我创建这个图书馆
  • 我是如何构建的(编码部分)
  • 最终详细信息(示例项目,单元测试,README.md,Cocoapods)
  • 要点和一般建议摘要(又名TLDR)

从一个明显的问题开始,我决定构建该库的原因是因为无论如何我通常最终都会将此功能集成到大多数项目中。 因此,我想我也可以使其更通用一些,然后将其打包到一个可以轻松添加到任何项目的库中,而不是在项目之间复制和粘贴代码。

构建此库对我来说也是一个机会,使我可以练习有关API的思考并练习构建模块化代码,同时隐藏实现细节。 我还必须使用泛型,而以前的任何项目都没有使用过泛型!

当我开始这个项目时,我的目标是建立一个简单的库,使您可以为类似于上面的gif的标签制作动画。 我最终在整个项目过程中添加了一些其他功能,尽管核心概念仍然相同。 这是我最终获得的功能列表:

  1. 仅使用一行代码即可开始制作动画
  2. 任何使用过UIView动画方法的人都熟悉的语法
  3. 接受任何数值作为最终值,您无需转换或打字
  4. 即使在同一标签中混有数字的文本也可以使用。 文本保持完整而数字动画化👍
  5. 完成处理程序可让您安全地将动画后的动作排队
  6. 您可以选择指定标签的动画曲线和小数点
  7. 可同时在UILabel和UITextField上使用(我打算稍后进行扩展)

标签动画是该库的主要目的,是我从Brian Voong的youtube视频中学到的东西。 在视频中,Brian讨论了CADisplayLink,以及如何使用它在UILabel中为文本设置动画以创建计数器和其他类似效果。 CADisplayLink正常工作,需要一些样板代码(包括选择器和@objc方法),而我认为该库可能会有用。

在继续之前,我想提到Daniel Kennett在Swift&Fika 2018上发表的关于API设计的演讲。 在该演讲中,Daniel谈到了API边界 ,这是API代码和用户代码之间的界线 。 作为API设计人员,您可以选择边界的去向,而这个决定可能会产生很大的影响。

边界越接近用户代码,您作为API设计人员要做的工作就越多。 作为回报,该API对于用户而言变得更加容易实现(请以其简单的安装过程来思考Crashlytics)。 丹尼尔展示了这张图片来传达这一点:

另一方面,如果您决定将边界放置在离代码更近的地方,那么最终您将不得不编写更少的代码,但是却为用户提供了更多的工作。 作为回报,他们通常最终会对API拥有更多控制权。 Daniel提供了Spotify元数据的示例:

我认为这两种方法都不对。 这完全取决于您要使用API​​来完成的工作。 就QuickTicker而言,我的目标是使用户入门尽可能简单,理想情况下是单行函数调用。 因此,我有兴趣制作与第一张图片(Crashlytics)更相似的图片。

该库的早期版本未使用专用类型。 相反,我将其构建为UILabel类型的扩展(如果您好奇它的外观,仍然可以在github的早期提交中看到它)。 因此,您可以直接在标签上使用点语法来调用API,如下所示:

 让someLabel = UILabel()someLabel.startTicker(持续时间:2,最终值:250) 

对于这种方法,我有些不满意的地方。 我不是扩展整个UILabel类型的忠实拥护者,因为当他们可能只需要为一个或两个标签设置动画时,我不想为用户污染所有UILabel的名称空间。 这种方法还意味着,如果不复制所有代码,就无法将相同的功能扩展到其他类型,例如UITextField。

顺便说一句,在早期的类型扩展试验中,我了解到实际上可以在Swift中为类型扩展添加存储的参数。 为此,您必须将计算所得的属性定义为关联的对象,然后使用关联键访问该对象,关联键是指向该关联的唯一指针。 最终结果如下所示:

构建示例应用程序时吸取了一些教训:

  1. 如果您打算创建一个Cocoapod(请参见下文),请不要手动构建示例应用程序。 让Cocoapods为您创建它,然后根据需要进行更改。 待会儿我会谢谢你的! 😃
  2. 对动画标签使用UIFont.monospacedDigitSystemFont可以防止在动画过程中晃动(尤其是在将文本与数字结合时)

这是一个有趣的。 我编写单元测试的经验有限; 绝对是我需要研究的领域😄

当我开始为该库编写测试时,我很快学到了两件事:

  • 在测试文件中,您需要在import语句之前添加@testable关键字,以便访问内部类
  • 您无法通过测试访问任何私有或文件私有方法,即使使用@testable也是如此

第二个对我来说很大。 那时,我所编写的所有类和方法都是私有的,除了我向用户公开的3种类方法。 我只想公开最低限度的最低要求,所以我不会用诸如QTObject之类的毫无意义的东西污染用户的名称空间。

但是为了测试QTObject内部的方法,我不得不删除私有限制,包括QTObject的初始化程序。 我在初始化程序中添加了一条注释,警告用户不要直接调用它,而应使用类方法。 后来,我发现由于这是一个内部类,因此如果您通过Cocoapods安装QuickTicker,则无论如何都无法直接访问它。 完善!

如果您好奇,这是我写的测试。 无论如何,它并不全面,但仍然可以使用。

在我之前提到的同样出色的Swift&Fika 2018大会上,Roy Marmelstein谈到了开源,他强调了为Github上的项目提供良好的自述页面的重要性。

好的自述文件首先说明了库是什么,以及您可以使用它做什么。 希望这通常很难,但使用此库很容易。 罗伊(Roy)建议创建自定义徽标,并添加屏幕截图,尤其是gif(如果适用于您的库)(例如,与UI相关)。

其他重要部分包括安装说明和要求以及示例项目。

我从一个自述模板开始,然后使用这些仓库来获取灵感:

  • 甜叶菊
  • SwiftyJSON
  • IGListKit
  • Alamofire
  • PagingKit
  • NSFWD检测器
  • YPImagePicker

接下来是Cocoapods。 之前,我在项目中广泛使用了Cocoapods,但从未真正创建过。 该过程最终非常简单。

要创建Cocoapod,我遵循了tutsplus的教程以及Cocoapod在其网站上自己的指南。

我想在这里重申一个建议,如果您打算在库中包含一个示例项目,请让Cocoapods创建初始版本(以及测试文件),然后根据需要进行修改。 我自己创建了该项目,然后在构建Cocoapod时以第二个项目结束,因此我不得不手动将文件从旧项目移动到新项目。 有点麻烦。

我想我已经涵盖了构建该库时我所经历的所有主要步骤! 这是一个有趣的周末项目,我在做这件事的同时当然学到了很多东西。 总结一些要点+一些一般建议:

  • 如果您要不断将某些功能不断添加到项目中,请考虑将其打包到一个开源库中
  • 开源可帮助您构建模块化代码并根据API进行思考
  • 在开始之前,请考虑要在哪里放置API边界
  • 如果您将边界放在靠近用户代码的位置(就像我对QuickTicker所做的那样),则希望您能做更多的工作
  • 尝试对您的设计进行过时的验证,并计划在将来进行附加更改,而不是在将来的更新中破坏用户的代码(例如,我添加了一个可以在不破坏任何现有代码的情况下安全扩展的选项数组)
  • 如果要添加示例项目,并且希望创建一个Cocoapod,请让Cocoapods为您创建项目+测试文件
  • 包括示例项目可帮助您生成屏幕截图(如果适用),并有助于将用户介绍给您的API
  • 不要忘记单元测试! 没有测试的图书馆的CocoaPods质量得分较低
  • 最后,拥有一个良好的README.md文件对于图书馆的成功至关重要!

希望本文启发您创建自己的开源库并与世界分享! 谢谢阅读!

如果您有任何疑问或建议,请发表评论或鸣叫@BesherMaleh

您可以在此处找到QuickTicker。

如果您感到好奇,这里有一些我使用此库的应用程序

谢谢阅读。 如果您喜欢这篇文章,请随时单击该鼓掌按钮👏,以帮助其他人找到它。 如果您*真的*喜欢它,您最多可以拍手50次😃