跨平台VIPER

曾几何时,我写过与得克萨斯州一样大的视图控制器,以为我已经很聪明地将表视图数据源移出了它们。 但是随着时间的流逝,我的大脑和心脏有些刺痛,说:“首席,必须有一种更好的编写软件的方法。 您需要提高您的游戏水平。”

那时是2014年的决定性日子,Mutual Mobile发表了一篇名为VIPER的文章。 我浏览了这篇文章,然后迅速而固执地向前迈进,而实际上并没有把VIPER弄得一团糟。

多年来,我开始发展自己的体系结构选择。 许多人说,虽然没有灵丹妙药,但我现在经常发现VIPER最适合我对具有清晰方向和长远眼光的应用程序的需求。 我非常喜欢模块化以及它如何消除iOS中单元测试的烦恼(大部分情况)。

在前往VIPER’ing的过程中,我并没有真正遇到过跨平台应用程序中完整而干净的实现示例。 因此,我最近决定编写Listy,这是一个适用于iOS,tvOS,watchOS和macOS的待办事项列表应用程序,也是我对开源软件whole的第一份全应用程序贡献。 我很想与您分享我的构建方式,并希望对您自己的软件开发冒险有所帮助。

如果您说的是:“看,太好了,但是我只想检查代码”,就可以开始了。

Listy的成立来自面试带回家的测试。 目的是编写一个简单的待办事项列表应用程序,该应用程序仅创建和删除列表和任务,但是花费大量的开发时间来构建一个项目基础,该基础将最适合在将来的开发中扩展该应用程序-本质上奠定了坚实的基础一棵小树的根直到一天都会长成一棵高大美丽的树枝。

不幸的是,我们最终并没有成为对手,但我还是决定继续自己开发Listy,并发表自己的发现,以创建在VIPER下运行的跨平台应用程序。 由于功能有限,我无法与Listy一起管理日常工作,但我认为它可以为其他项目的开发提供漂亮的样板房/参考指南!

与我从头开始或重构的大多数iOS应用一样,我通常要做的第一件事是创建一个基本的Cocoa Touch框架,以容纳主要的纯Swift和Foundation框架API交互-自定义数据结构,Swift数据结构的扩展,模型和数据持久性-我们应该能够在应用扩展和其他任何地方使用和访问的东西。

然后,我创建第二个Cocoa Touch框架来处理所有常见的UI(Kit)交互-常见的视图(即视图控制器),表视图数据源/代理,UI数据结构便利性扩展-我们可以在许多不同的用例中使用的东西。

VIPER在哪里适合? 通常,我的VIPER模块全部都位于iOS主应用和扩展程序中。 随着跨平台的发展,将蛇分开的意义何在何处?

VIPER的演示者和交互者甚至不应该考虑触摸UIKit。 演示者应该知道何时(通过视图和路由器)呈现和更新UI,并且交互者应处理业务逻辑和CRUD’ing实体¹。 鉴于列表和任务管理的演示者和交互者行为有多么相似,我将它们全部放入基本框架ListyKit中。²

在所有Listy应用程序的基础上都有演示者和交互者,在任何应用程序中,我都可以做以下整洁的事情:

  • 获取列表和任务数据,通过它们可以更新列表和任务视图。
  • 告诉列表和任务视图进行自我更新,删除表视图行以及显示错误警报。
  • 让路由器准备显示编辑列表和编辑任务视图。

我还将所有路由器输入和输出协议都放置在ListyKit中,但没有放置任何实际的路由器。 我曾考虑过包括路由器,但由于路由与平台之间的差异很大,因此可能会有些过时。 例如,当用户指示要添加任务时,在iOS上,我们会显示一个完整的单独视图,而在macOS上,我们会将带有文本字段的表视图行添​​加到任务列表中。 您可以使用预处理程序条件来加载不同的平台特定代码(并且我确实在应用程序的其他地方使用了一些代码),但是感觉到这样做可能会使路由器变得有些毛茸茸。 (而且,太多的东西使东西闻起来很臭。)

在三种不同的设备之间,iPhone,iPad和Apple TV在UI API方面有很多共同点。 因此,我让ListyUI坐在ListyKit的顶部,并提供以下内容:

  • 设置,配置和更新所有列表和编辑视图。
  • 在所有列表和任务视图中重用表视图数据源和单元配置。
  • 共享视图路由输出实现(例如,呈现/推动/关闭视图)。
  • 出现错误警报

在每个视图的updateView(…)函数中,我执行单元配置,选择和删除的基于块的设置。 这使我可以将表视图数据源和委托与视图分开,同时在用户采取操作时仍能够根据需要调用演示者。

好吧,它们特别特别,因为它们具有WKInterfaceControllers和NSViewControllers之类的奇特功能,而我们的朋友iOS和tvOS则没有。 从理论上讲,虽然我们可以在所有平台上创建一些疯狂的主视图抽象,但我认为最好只为watchOS和macOS分别处理UI交互。 毕竟,我们仍然可以从ListyKit获得VIPER中的“ IPE”,它可以解决许多其他繁重的工作。

最初,我认为我可以使ListyKit和ListyUI跨平台工作,但是没有。 我很快发现跨平台框架在公园里走不动。 幸运的是,我发现了这个非常不错的指南,该指南帮助我了解了如何通过特定于平台的框架触摸共享的Info.plist文件来共享代码(设置细节不在本文讨论范围之内,但是我建议您将该文章阅读一下以了解学到更多)。

我最终建立了以下特定于平台的框架:

  • ListyKit iOS
  • ListyKit tvOS
  • ListyKit watchOS
  • ListyKit macOS
  • ListyUI iOS
  • ListyUI电视

每个框架通过共享“构建设置”中的上述Info.plist路径和文件目标成员资格配置,连接到相同的代码。 也就是说,每个文件仍必须根据需要与每个平台框架手动关联。 例如,ListsInteractor.swift必须具有ListyKit iOS,ListyKit tvOS,ListyKit watchOS和ListyKit macOS的目标成员身份。 如果苹果推出了一款新的增强现实设备,那么,您可能需要花一些时间检查ListyKit增强型OS的目标成员资格框-还不错。

我敢肯定,我的VIPER实施方案并不是很完美。 如果您发现您认为可以改进的地方,我会全力以赴。 实际上,我欢迎您的贡献! 我计划在这本VIPER特定的文章之后,再跟其他有关我如何制作Listy的文章,涵盖诸如抽象视图路由输出和呈现错误警报之类的细节。 另外,如果您喜欢并想要使用Listy代码,请带它并随心所欲地运行它(它仍然具有许可证,哟)。 感谢您抽出宝贵的时间阅读我的文章,希望对您有所帮助。 欢迎在评论中提出问题或想法! GitHub上的Listy

  • 使用VIPER架构iOS应用
  • 为iOS,watchOS和tvOS创建跨平台Swift框架

¹从技术上讲,不应将实体从交互器传递回演示者,而应创建与用例关联的简单数据结构。 但是,鉴于Listy中的当前实体非常小而简单,因此我决定暂时打破此规则。

²人们可能会认为演示者之所以属于ListyUI,是因为尽管与UIKit无关,但它们是与UI相关的功能。 虽然我同意,但我觉得这是一个更好的折衷方案,使它们更容易从ListyKit中重用。 如果将来Listy的演示逻辑变得更加复杂/特定于平台,我将相应地将其移至单个应用程序代码。