MVVM->避免“紧急汤”

设置

在过去的几年中与MVC和MVP在可可粉接触领域合作之后,我认为现在应该退后一步,考虑MVVM方法。 最近,我在Ben DiFrancesco上观看了一次演讲,他在会议上讨论了如何以及为何在项目中采用MVVM。 Ben解释了如何在您的ViewControllers中添加目标/动作,委派,通知,KVO和块(Swift中的闭包)实际上只是创建了这种“命令性汤”,在此情况下,您需要为业务逻辑和其他代码做出复杂的推理。 此外,由于您与特定于平台的框架紧密耦合,因此将代码测试和定位到其他平台变得更加困难。 从经验上来讲,当您想开始向应用程序添加扩展时,这绝对成为问题。 确保您的所有业务逻辑都适用于Today,Watch和其他扩展, 并且如果不小心处理实现您的主应用程序将是一场噩梦。

回顾MVC

对于iOS中的MVC,控制器具有共同的职责; 表示逻辑与业务逻辑混合在一起。 这创造了Ben谈论的当务之急:“最终结果是使代码难以理解,难以测试并且[通常]丑陋的代码。”

让我们看一下MVVM如何尝试解决此问题。

MVVM概述

使用MVVM,我们引入了一个称为ViewModel的新对象,该对象封装了表示逻辑。 ViewModel使用一个模型或一组模型,并创建已预先格式化以在视图上呈现的属性。 Ben给出的示例是格式化字符串,确定子视图的颜色,设置显示/隐藏布尔值或确定表视图中的行数。 使用这种模式,ViewController的工作变得简单得多,因为它只是将ViewModel属性连接到视图属性。

示例应用

本着学习新事物的精神,我们将通过制作示例应用程序来测试MVVM技能。 想象一下,您刚刚被一家摄影公司聘用为新职位,他们委托您制作一个应用程序来展示公司的代理商目录及其投资组合。 他们决定使用Portfolious这个名字,是因为它在时髦/时髦的焦点小组中表现很好。 无论如何,在Portfolious中,您将拥有一个座席列表,其中包含与他们有关的数据以及他们的工作组合。 首先,您将使用此JSON占位符数据的子集和最近启用的最新网络库。 您可以在Github上签出完整的项目。

应用布局

使用Sketch,我们将创建一个视图来展示Portfolious的主题。 我们将有一个座席的表格视图,其中每个单元格将具有一些基本信息,例如其名称。 进入代理后,您将获得扩展的详细信息集(由用户模型提供)。 在此下方,我们将看到其相册的表格视图,其中每个相册将呈现该相册中照片的集合视图。

资料模型

对于我们的数据源,我们将使用三个主要模型:用户,相册和照片。 每个用户都是该目录中的一名员工,当您进入他们的个人资料时,我们会在其中添加他们最喜欢的照片的相册。

用户的定义如下:

  { 
“ id”:1
“ name”:“ Leanne Graham”,
“用户名”:“布雷”,
“电子邮件”:“ Sincere@april.biz”,
“地址”: {
“街道”:“库拉斯之光”,
“ suite”:“ Apt。556”,
“ city”:“ Gwenborough”,
“邮政编码”:“ 92998-3874”,
“ geo”:{
“ lat”:“-37.3159”,
“ lng”:“ 81.1496”
}
},
“ phone”:“ 1-770-736-8031 x56442”,
“网站”:“ hildegard.org”,
“公司”:{
“ name”:“ Romaguera-Crona”,
“ catchPhrase”:“多层客户端-服务器神经网络”,
“ bs”:“利用实时电子市场”
}
}

这是专辑:

  { 
“ userId”:1
“ id”:1
“ title”:“ quidem molestiae enim”
}

以及这些相册中的照片:

  { 
“ albumId”:1
“ id”:1
“ title”:“ accusamus beatae ad facilis cum similique qui sunt”,
“ url”:“ http://placehold.it/600/92c952”,
“ thumbnailUrl”:“ http://placehold.it/150/30ac17”
}

为了访问这些JSON模型,我们将使用前面提到的该网络库。 要将其添加到我们的项目中,我们将使用迦太基。 将以下行添加到项目目录顶层的Cartfile中。

  github“ KevinVitale / ReactiveJSON”“大师” 

接下来,在项目目录中运行以下命令以构建框架。

 迦太基更新-平台iOS 

将Carthage产生的框架拖放到主要目标的Embedded Binaries区域。

并将此运行阶段脚本与框架一起添加为输入文件。

如果我们运行该项目,我们应该会得到一个空白屏幕,但是我们的框架已成功添加。

添加模型

我们要做的第一件事是创建一些应用程序将使用的数据模型。 每个模型都是一个结构,其结构基于我们从JSON占位符获得的JSON。 每个模型都符合等价协议,并实现自己的==函数。

API客户端控制器

我们将添加一个名为JSONPlaceholderClient.swift的网络客户端,它将包含一些代码以连接到JSONPlaceholder服务

 进口基金会 
导入ReactiveJSON

公共结构JSONPlaceholder:JSONService,ServiceHostType {
私有静态let _sharedInstance = InstanceType()
// ------------------------------------------------ --------------------------
//协议:JSONService
公共类型别名InstanceType = JSONPlaceholder
公共静态函数sharedInstance()-> InstanceType {
返回_sharedInstance
}
// ------------------------------------------------ --------------------------
//协议:ServiceHostType
公共静态var方案:字符串{return“ http”}
公共静态var主机:字符串{返回“ jsonplaceholder.typicode.com”}
公共静态var路径:字符串? {return nil}
// ------------------------------------------------ --------------------------
}

现在,我们有一些代码可以与远程JSON服务对话,我们可以开始考虑如何为模型添加有希望的JSON。 我们将使用Argo接收收到的JSON,以便将其编码到模型中。 要使用迦太基将Argo,Curry和Runes(Curry和Runes是Argo依赖项)添加到项目中,我们需要将以下这些行添加到Cartfile中:

  github“ thoughtbot / Argo” 
github的“ thoughtbot /符文”
github“ thoughtbot / Curry”

并运行

 迦太基更新-平台iOS 

建立计划。

构建它们之后,我们需要像以前使用ReactiveJSON一样将它们添加到嵌入式二进制文件中。

接下来,我们需要像以前一样将Argo添加到嵌入式二进制文件中并运行脚本。 如果一切正常,我们应该能够将Argo导入模型以允许JSON解码。

 扩展用户:可解码{ 
静态函数解码(json:JSON)->解码后的 {
返回咖喱(self.init)
json <| “ID”
json <| “名称”
json <| “用户名”
json <| “电子邮件”
json <| “地址”
json <| “电话”
json <| “网站”
json <| “公司”
}
}