使用VIPER架构构建Todo List iOS应用

介绍

确定构建iOS应用程序时使用哪种应用程序架构是最具挑战性的任务之一,可以从MVC,MVVM,MVP,View State,VIPER等众多架构中进行选择。 我们选择的体系结构将决定软件的构建方式以及随着软件的扩展而扩展。

本文将介绍的架构之一是VIPER架构。 VIPER以单一职责原则将应用程序结构划分为模块/屏幕内的组件。 这使应用程序变得更加模块化,并且与其他组件的耦合更少。 由于每个组件之间的边界(协议/接口),单元测试和集成测试变得更加简单。

VIPER的基本组件分为5部分:

  • 视图:显示演示者告诉的用户界面,还将用户输入传达回演示者。
  • 交互器:处理应用程序的业务逻辑,它与演示者进行来回通信
  • 演示者:从交互器获取数据,并处理如何在视图中显示数据的逻辑。 它还从视图中继用户输入,并从交互器获取/更新数据。
  • 实体:交互器使用的模型对象。 通常,交互器从单独的数据存储对象中获取实体。
  • 路由/线框:处理演示者对象询问的导航逻辑。 它与要显示的其他模块/屏幕进行通信。

使用VIPER构建我们的待办事项列表应用

在本文中,我们将使用VIPER作为我们的应用程序体系结构构建一个简单的TodoList应用程序。 项目GitHub存储库可在此处获得。 我们将建立:

  • TodoItem实体和TodoStore:TodoItem是代表Todo项的基本Class对象,TodoStore是存储TodoItem数组的DataStore。
  • TodoList模块/屏幕:向用户显示UITableView中TodoItem的列表,并为用户提供添加新TodoItem,删除TodoItem以及导航至TodoDetail模块/屏幕的功能。
  • TodoDetail模块/屏幕:显示TodoItem的内容,为用户提供删除和编辑TodoItem的功能。 它导航回到TodoList模块/屏幕。
  • 应用程序委托集成:通过从TodoListRouter实例化TodoListView来设置应用程序的根UIViewController

建筑数据实体

TodoItem实体

TodoItem实体只是代表TodoItem对象的普通类。 它提供2个属性,标题字符串和内容字符串。

TodoItem实体

TodoStore数据存储

TodoStore是存储TodoItem列表的DataStore Singleton对象。 我们的应用程序只是将数组存储在内存中,但是将来我们可以扩展以将数据存储在File或CoreData中。 它通过todos属性和方法公开TodoItem数组,以添加TodoItem并删除TodoItem。

TodoStore

构建TodoListModule / Screen

TodoListModule协议

我们为每个组件使用协议来定义每个组件如何在TodoList模块中进行通信的边界。

TodoListModule协议

实现TodoListViewProtocol

我们创建一个子类UITableViewController的TodoListViewController对象,并实现TodoListViewProtocol。 TodoListViewController的职责是按照演示者的指示显示用户界面。 它保留对演示者的引用,以中继用户输入并查看生命周期事件以使演示者做出反应。

当视图出现时,它将调用演示者viewWillAppear方法,以便演示者可以从交互器检索数据。 导航添加栏按钮项触发一个操作,该操作将显示带有2个文本字段的UIAlertActionController,供用户输入TodoItem的标题和内容。 然后,它将用户输入中继回演示者。 当用户滑动UITableViewCell并删除该行时,该视图会将用户输入重定向以将关联的ToDoItem删除回呈现给演示者。

TodoListViewProtocol提供2种方法来实现,showTodos传递ToDoItem数组,该数组将用于在UITableView内部显示TodoItem的列表。 如果演示者发生错误,showErrorMessage会传递一条错误消息,该UIAlertController将显示给用户,其中包含错误消息。

TodoList视图

实现TodoListPresenterProtocol

TodoListPresenter类实现TodoListPresenterProtocol和TodoListInteractorOutputProtocol。 它存储对TodoListViewProtocol的弱引用,因此可以更新UI。 它存储对TodoListInteractorInputProtocol的引用,因此演示者可以中继用户输入以通过交互器获取或修改数据。 它还存储TodoListRouterProtocol对象,以便当用户从表视图中选择TodoItem时可以导航到TodoDetailModule。

通过实现TodoPresenterProtocol,当视图以screeen出现时,该视图将调用演示者viewWillAppear。 然后,演示者要求交互者检索TodoItem数组。 交互器将通过调用didRetrieveTodos传递数据来使用TodoListInteractorOutputProtocol的实现来返回TodoItem数组,演示者然后通过调用showTodos传递数据以更新表视图的方式更新视图。

演示者还实现了didAddTodo和didRemoveToDo,因此当用户添加新的TodoItem或从视图中删除TodoItem时,演示者可以将用户操作中继到交互器各自的saveTodo和deleteTodo方法。 交互者将调用didAddTodo和didRemoveTodo返回给演示者,以便演示者可以更新表视图。

TodoList演示者

实现TodoListInteractorProtocol

TodoListInteractor实现TodoListInteractorInputProtocol。 它存储对演示者对象的引用,该演示者对象可以实现TodoListInteractorOutputProtocol。 它还将TodoStore对象分配为属性,以获取TodoItem列表,添加TodoItem,从TodoStore中删除removeTodoItem。

TodoListPresenterProtocol调用该操作以检索TodoItem列表,保存TodoItem,删除TodoItem,然后交互器向TodoStore询问每个相应的操作,然后将输出方法返回给演示者。

TodoList交互器

实现TodoListRouterProtocol

TodoListRouter公开了可以调用的静态函数,以实例化TodoListModule,实例化TodoListProtocols组件的具体实现,为每个组件分配引用,然后返回要显示的UIViewController。

它还提供了presentTodoDetailScreen,当用户从TableView中选择TodoItem时,它将由TodoListPresenter调用。 该方法实例化TodoDetailModule,它从TodoDetailRouter静态方法传递TodoItem,并通过将UIViewController推入UINavigationController堆栈来导航到TodoDetailViewController。

TodoList路由器

构建TodoDetailModule / Screen

TodoDetailModule协议

就像TodoListModule一样,协议定义了每个组件如何在TodoDetail模块中进行通信的边界。

TodoDetailModule协议

实现TodoDetailViewProtocol

TodoDetailViewController是实现TodoDetailViewProtocol的UIViewController子类。 加载视图后,它会为演示者调用TodoDetailPresenter viewDidLoad方法,以要求交互者获取ToDoItem以便在UI中显示。

演示者将通过传递给视图的TodoItem来调用showTodoItem,以使用UILabels显示TodoItem标题和内容。 当按下按钮时,该视图还会将用户操作传递给编辑和删除操作,以演示者的deleteTodo方法进行。 对于编辑操作,将显示UIAlertController,其中包含用当前TodoItem标题和内容填充的TextField,以供用户修改。 当他们确认后,该视图将用户输入从文本字段传回给演示者,并传递标题和内容的新值。

TodoDetail视图

实施TodoDetailPresenterProtocol

TodoDetailPresenter实现TodoDetailPresenterProtocol和TodoDetailInteractorOutputProtocol。 加载视图时,演示者向交互器询问TodoItem,然后通过调用showTodo要求视图显示TodoItem。

它还从视图处理用户动作的中继,以将TodoItem编辑和删除到交互器。 在交互器成功编辑项以基于新值更新视图之后,TodoDetailInteractorOutputProtocol的实现将处理结果,对于该删除,由于TodoItem已被删除,它调用TodoDetailRouterProtocol导航回TodoListView。

TodoDetail演示者

实现TodoDetailInteractorProtocol

TodoDetailInteractor实现了TodoDetailInteractorInputProtocol,它还引用了TodoStore和TodoItem对象。 演示者可以通过访问属性从交互器检索TodoItem。 它还提供了删除TodoItem和编辑TodoItem的方法。 delete操作将通过TodoItem调用TodoStore removeTodo,以便可以从TodoStore中删除TodoItem。

TodoDetail交互器

实现TodoDetailRouterProtocol

TodoDetailRouter实现TodoDetailRouterProtocol。 它提供了静态方法来实例化TodoDetailModule,设置所有组件并将它们关联,然后返回UIViewController进行显示。 它还公开了当用户从TodoDetail屏幕中删除TodoItem时将调用的NavigationBackToListViewController,以便在删除TodoItem之后向后导航。

TodoDetail路由器

AppDelegate设置和集成

AppDelegate的设置非常简单直接,在applicationDidFinishLaunchingWithOptions内部,我们通过调用设置TodoListModule组件的TodoListRouter.createTodoListModule实例化TodoListViewController。 然后,我们实例化UIWindow并将ViewController作为窗口的rootViewController分配给我们的初始屏幕。

应用程序委托

结论

通过使用VIPER构建iOS应用程序,可以通过将每个应用程序层的职责划分为单独的组件来提供干净的架构范例,这使我们的应用程序在视图逻辑和业务逻辑之间成为模块化,并且在大型应用程序中能够使我们的应用成为优势组件模块化,因此我们可以更轻松地添加更多功能。

VIPER的主要缺点之一是,我们需要为每个组件协议编写太多样板代码,这在小型应用程序中是一个过大的杀伤力。 最后,是否在我们的项目中使用VIPER的决定取决于我们的项目规模,我们的团队使用它的熟悉程度以及我们特定于应用程序的用例。