HandsAppMVP:用于工作室开发外包的iOS体系结构

图片

好的代码始于架构,iOS应用程序也不例外。有很多标准模式,但是本文的目的不是讨论它们,而是讨论改编其中一种并开发自己的经验。我们称这种改编为HandsAppMVP。

图片

在iOS开发中,该架构主要确定一个特定ViewController的类和依赖项的组织。但是,核心组件不仅可以是他,还可以是UIView。选择取决于特定任务。

架构比较


有几种适用于iOS的标准体系结构模板:MVC,MVP,MVVM,VIPER(可在本文末尾找到每个描述的链接)。

选择用于开发的体系结构,我们确定了应与之相对应的主要参数:开发速度,灵活性和较低的入门门槛。接下来,我们在考虑了这些参数的情况下进行了三种著名架构的比较(由于完全不遵守单一职责,MVC iOS社区模板长期被埋没了)。

对于外包团队而言,开发速度尤为重要。 VIPER是最复杂且“缓慢”的体系结构;使用纯MVP或MVVM可以加快开发速度,因为它们的组件更少。

灵活性意味着轻松添加或删除应用程序中的功能。该参数与应用程序生命周期各个阶段(最初阶段除外)的开发速度密切相关。灵活性也与测试的简便性密切相关-自动测试使开发人员确信他不会破坏任何东西,并有助于避免错误。测试无法涵盖经典MVP,特别是如果您不使用下面讨论的类接口,则尤其如此。从测试的角度来看,MVVM的性能也很差,因为测试反应式代码需要更长的时间。 VIPER非常适合编写测试,因为它尽可能地尊重唯一责任的原则,并且类依赖于抽象。

我们考虑的最后一个参数是进入阈值。它显示了新开发人员(首先是琼斯)如何迅速渗透到体系结构中。在这里,使用第三方反应式库(RxSwift,PromiseKit等)的MVVM由于明显的原因而排在最后。由于组件数量众多,VIPER还是一个相当复杂的体系结构。MVP具有最低的进入阈值。

在权衡利弊之后,我们得出的结论是,我们需要像MVP一样简单而像VIPER一样灵活的东西。因此,这个想法诞生了,以它们为基础创建自己的架构-HandsAppMVP。

扩大MVP


我们架构的主要组件是模型,视图,演示器。它们根据众所周知的方案执行与经典MVP中相同的功能:

图片
[经典MVP的方案]在

下文中,在图中,每个交互组件(蓝色方块)都是其寿命与View寿命一致的类。实线箭头表示对另一个对象的所有权,强链接,而虚线箭头表示弱链接。使用弱引用,我们可以防止循环依赖和内存泄漏。

介面


首先,我们将ViewInput和ViewOutput接口添加到此经典架构。我们考虑了SOLID的第五条原则-依赖倒置的原则。它更可能不是增加的,而是MVP的改进。对抽象的依赖有助于摆脱组件之间的严格联系,并允许您正常编写测试。考虑接口的方案如下所示:

图片
[添加ViewInput和ViewOutput接口]一个

小矩形是一个接口。

细心的开发人员会问,模型的接口在哪里?现在我们转向他们。

处理数据


移动体系结构中的数据模型是一个统一的概念。一个标准示例:应用程序断开网络以与服务器进行交互,然后将数据保存在CoreData中以供脱机工作,将一些简单信息写入UserDefaults并将JWT存储在钥匙串中。与之交互的所有这些数据组成了模型。

负责与特定类型的数据容器进行交互的类,我们称为数据服务。对于每个容器(远程数据库,本地数据库,UserDefaults等),将一个服务类添加到HandsAppMVP中,该类与演示者进行交互。现在,您还可以为每个数据服务添加输入/输出接口:

图片
[添加用于处理数据的服务]

并非每个服务类都需要使用接口连接到演示者,例如在使用Moya时。Moya是一个开源网络库。Moya提供了现成的服务类(MoyaProvider),并且在编写测试时,我们不必创建替代ApiProvider的模拟对象。Moya提供了一种特殊的测试模式,当打开时,MoyaProvider不会敲打网络,而是返回测试数据(可以在此处找到更多详细信息)。在这种情况下,演示者不是引用MoyaProvider抽象,而是引用实现。并且我们使用闭包从此服务中获得反馈。可以在演示项目中找到示例实现。

该示例比规则更多的是例外,它表明遵守SOLID并不总是最好的解决方案。

导航


我们将应用程序中的导航视为单独的责任。HandsAppMVP为此使用一个特殊的类-路由器。路由器包含一个到View的弱链接,通过它可以显示新屏幕或关闭当前屏幕。路由器还使用RouterInput接口与演示者进行交互:

图片
[添加导航组件(路由器)]

组件组装


我们使用的经典MVP的最后一个补充是Assembly(收集器类)。它用于初始化View和HandsAppMVP的其他组件,以及实现依赖关系。程序集包含唯一的公共方法-“ assemble()-> UIViewController`”,其结果是所需的UIViewController(或UIView)以及必要的依赖关系图。

我们不会在体系结构图上显示Assembly,因为它没有与MVP组件连接,并且其生命周期在创建后立即结束。

代码生成


为了节省时间,我们使用Generamba自动化了创建HandsAppMVP类的过程。可以在我们的存储库中找到用于Generamba的模板。Generamba的示例配置在演示项目中。

生成特定屏幕的结果是,我们获得了与HandsAppMVP方案相对应的一组类,用于创建和实现组件的一组单元测试以及用于演示者测试的模板类。

发生了什么


如果比较Head-to-Head HandsAppMVP和VIPER,您会发现它们非常相似,第一个区别仅在于缺少Interactor组件。但是,摆脱了服务与当前(交互器)之间的层,以及使用Moya简化了与网络的交互,我们收到了显着的开发速度提高。

我们建议您在设计阶段对体系结构给予足够的重视,以避免出现全球性错误,与客户的纠纷和将来对开发人员的折磨,而以能干且可预测的方式领导开发过程。

请记住,任何架构可能都不特别适合您的项目,因此不要急于盲目地依赖现成的模板及其成功应用案例。不要害怕开发和应用您的解决方案-与现成的解决方案相比,它们对您而言变得更有价值和更灵活。

总之,我们将推荐一些有关iOS应用程序体系结构的好文章,这些文章可以帮助我们理解复杂性并做出选择:

  1. iOS中的架构模式
  2. iOS Swift:MVP架构
  3. 在Swift 4上的一个小型iOS应用程序示例上分析VIPER架构
  4. 使用RxSwift在iOS上实现MVVM

SurfStudio 开源文档也非常有帮助和启发

最后,我们将链接建立到在HandsAppMVP中编写DEMO项目上,我们在本文中已多次提到。

All Articles