前提回忆
在前几个章节中,咱们已经完结了Linkworld的基本功用,但要到达最小MVP产品还有些距离。完结的功用现在只能在模拟器中运用,短少本地化存储、网络恳求和修改、删去等功用,这尚未到达最小MVP的要求。
MVP是最小的可用产品,其核心在于最小和可用。“最小”代表着该产品能够反复迭代且迭代周期短,“可用”代表该产品是能够满意用户预期的能够交给的产品,而不仅仅是demo。
因而咱们持续深入完善Linkworld这款产品,完结最小MVP产品。
MVVM架构形式:Model-View-ViewModel
通常在SwiftUi开发过程中,开发者们会运用MVVM开发架构形式进行项目树立。MVVM全称为Model-View-ViewModel,即模型-视图-视图模型。在该架构形式中,咱们树立数据模型将数据进行抽象和分离,然后独自创立视图,最后运用ViewModel进行双向绑定链接数据和视图。
在本项目中,咱们创立的Model文件就是Model模型部分,而ContentView、NewView、HomePageView是View视图部分,而咱们完结的独自抽离ViewModel模型视图部分内容,因而在视图与视图之间,视图与模型之间都树立了多次的变量声明和双向绑定,示例:indexURL。
下面咱们来创立ViewModel视图模型并在该项目中运用它,创立一个新的Swift文件,命名为ViewModel,如下图所示:
为了便于文件办理,咱们能够创立对应的文件夹用于寄存不同内容的文件,使得文件目录愈加清晰明晰,如下图所示:
ViewModel模型视图文件作为链接数据模型和视图之前的“联络官”,需求给视图传递来自于Model的数据,因而咱们能够创立一个结构体,遵从ObservableObject协议,ObservableObject协议能够在数据更新时告知视图更新内容。
然后在ViewModel模型视图内,咱们运用树立发布器来创立来自Model的数组结构,如下代码所示:
import Foundation
import SwiftUI
class ViewModel:ObservableObject {
@Published var models = [Model]()
}
URLSession实例:创立网络恳求
创立好ViewModel模型视图后,咱们来学习运用URLSession创立一个网络恳求,在学习之前咱们先来了解一个JSON文件的相关知识。
JSON是一种文件格局,和TXT文本相同,JSON运用带有固定格局的纯文本创立数据集。咱们在树立NewView增加身份卡视图时曾完结了新增身份卡的办法,即赋值给契合Model格局的变量,然后将Model格局的数据增加到个人主页的List中。
JSON文件则是包含了一切契合Model格局的数据的数组集合,咱们能够创立好一个JSON文件及其内容,并经过网络恳求将JSON文件恳求到本地并解析加载到List中。
咱们先来看看JSON文件的格局,如下图所示:
[ { "platformIcon": "icon_juejin", "title": "签约作者", "platformName": "稀土技能社区", "indexURL": "/user/3897092103223517" }, { "platformIcon": "icon_aliyun", "title": "专家博主", "platformName": "阿里云社区", "indexURL": "developer.aliyun.com/profile/expert/376pj7xeqgqjy" }]
上述JSON文件是经过一个在线JSON文件生成器(如npoint.io、fastmock.site等等)生成的准备代码,其间由“[]”包裹的是数组,而“{}”包裹的是数组中的方针,咱们创立了2个方针,且数据模型格局都契合Model数据模型界说的参数。
经过第三方网站能够生成一个API接口的链接地址,咱们声明一个常量参数存储它,如下代码所示:
let JsonURL = "https://api.npoint.io/c51968c99a2a087dac5e"
当然,咱们也能够运用测验东西测验创立的JSON文件地址返回的结果是否正确,能够运用postman或者apifox测验软件进行测验,防止在项目开发时恳求数据失利而导致开发卡点的问题,如下图所示:
上图中,运用的测验东西是apifox。运用办法也很简单,创立一个接口并将刚刚生成的地输入进去,并设置好对应的Query参数,点击运转,便可查看该接口返回状态及结果。娴熟运用接口测验东西,能够帮助咱们节约开发时间。
回归正题,咱们在ViewModel视图模型中运用URLSession实例创立一个网络恳求的办法,如下代码所示:
//网络恳求
func getData() {
let session = URLSession(configuration: .default)
session.dataTask(with: URL(string: JsonURL)!) { data, _, _ in
guard let jsonData = data else { return }
do {
let data = try JSONDecoder().decode([Model].self, from: jsonData)
self.models = data
} catch {
print(error)
}
}
.resume()
}
上述代码中,咱们创立了一个网络恳求办法getData,在办法里,咱们运用URLSession创立网络恳求,声明晰常量session来运用URLSession API,然后加载的方针地址为声明的JsonURL,并执行下载数据,当下载成功后,将依照Model数据模型格局的数组加载到models数组里,若加载失利则打印错误信息。
然后咱们能够在初始化时调用getData取得数据办法来加载数据,如下代码所示:
init(){
getData()
}
因为运用JSONDecoder编码器,因而Model数据模型还需求设置遵从可编码性 Codable 协议,才干防止报错。又因为id在JSON文件中不存在,因而需求告知编译器运用某些CodingKeys,不包括id。如下代码所示:
enum CodingKeys : String, CodingKey {
case platformIcon, title, platformName, indexURL
}
上述代码中,咱们运用枚举类CodingKeys,用来指定Model数据模型中应该接纳哪些数据,用于扫除恳求JSON时短少ID的问题。别的CodingKey也能够作为关键字替换的办法,当实际开发过程中后端与前端界说的字段称号不相同时,也但是运用该办法进行字段对照映射,这点后面有时间会讲到。
ViewModel模型视图创立完结后,咱们来到ContentView视图,要运用ViewModel模型视图链接Model和View,咱们需求运用新增一个状态变量,如下代码所示:
@StateObject var viewModel = ViewModel()
声明状态变量viewModel作用是初始化ViewModel并交由SwiftUI进行办理存储,这样咱们就能够在View视图中运用ViewModel形式视图中的一切办法和变量。
紧接着,咱们将本来在List运用的用@State声明的models数组替换成viewModel中的数组models,如下图所示:
如此,经过URLSession API,咱们从网络恳求了JSON文件数据,并把它解析到了LIst中并展示出来了。如下图所示:
办法优化:优化“增加身份卡”办法
接下来咱们来优化增加身份卡的办法,在ViewModel模型视图中,咱们运用@Published声明晰数据模型数组models,并在ContentView页面中替换了运用@State声明的models数组。
因而咱们能够将一切网络恳求、新增、修改、删去的办法都放在ViewModel模型视图里,然后在一切页面树立链接,新增办法如下代码所示:
// 创立身份卡
func addCard(newItem: Model) {
models.append(newItem)
}
上述代码中,咱们创立了一个办法addCard,经过传入契合Model数据模型格局的常量newItem中,然后将newItem增加到数组models中,完结增加数据的动作。
接下来回到 NewView中,咱们引用ViewModel视图模型,并替换本来运用@Binding双向绑定声明的数组models,如下代码所示:
var viewModel:ViewModel
这时候可能会报错,没有关系,这是因为本来@Binding双向绑定声明的数组models树立的关系被破坏掉了,而为什么不像ContentView那样运用@StateObject var viewModel = ViewModel()声明,这是因为在增加页面,增加的数据要加载到ContentView的List列表中,进行数据之间的传递。
增加身份卡的办法需求替换原有的models,换成viewModel视图模型中的addCard办法,如下代码所示:
self.viewModel.addCard(newItem: newItem)
而且NewView页面预览时,需求赋予变量viewModel值,如下代码所示:
NewView(viewModel: ViewModel())
上述代码中,咱们运用ViewModel视图模型代替纯Model数据模型的数组,并将本来的增加身份卡办法替换为ViewModel创立好的addCard办法,为了预览当时NewView页面,还需求给NewView声明的viewModel变量赋予默认值ViewModel()。
在NewView页面调整后,因为之前咱们还在引用NewView视图的相关页面进行了绑定,这时候要回到ContentView视图中,免除之前的绑定,如下图所示:
NewView(viewModel: viewModel)
如此,咱们便完结了运用Model-View-ViewModel架构形式调整原有项目代码,并完结引用ViewModel视图模型中的办法来创立视图,做到将页面和数据分隔开,使得代码进一步精炼。
办法新增:增加“删去身份卡”的办法
调整完“增加身份卡”办法后,咱们来弥补“删去身份卡”办法。删去身份卡的办法和增加身份卡办法相同,咱们能够在ViewModel视图模型中创立,如下代码所示:
// 删去身份卡片
func deleteItem(itemId: UUID) {
models.removeAll(where: {$0.id == itemId})
}
上述代码中,咱们创立了一个删去身份卡片的办法deleteItem,传入身份卡片数据的UUID,作为索引找到对应的身份卡片,然后调用removeAll办法,删去models数组中对应UUID对传入的UUID的数据,完结删去数据的操作。
回到ContentView页面,咱们创立一个简单的删去身份卡的交互,运用contextMenu上下文菜单按钮树立删去交互,如下代码所示:
// 卡片视图
CardView(platformIcon: item.platformIcon, title: item.title, platformName: item.platformName, indexURL: item.indexURL)
// 长按唤起删去
.contextMenu {
Button(action: {
self.viewModel.deleteItem(itemId: item.id)
}, label: {
Text("删去")
})
}
上述代码中,咱们给CardView卡片视图增加了一个上下文菜单的交互操作,当点击CardView卡片时,唤起上下文菜单,点击菜单按钮调用viewModel视图模型的deleteItem删去办法,指定删去的卡片id为点击卡片的UUID。
咱们长按点击卡片预览下作用,如下图所示:
上面的办法是运用contextMenu上下文菜单完结身份卡片的办法,比较简单。
项目预览
完结后,咱们在模拟器中预览下作用,如下图所示:
项目小结
在本章中,咱们学习了Model-View-ViewModel模型-视图-模型视图架构形式进行项目的开发,将运用URLSession API完结网络恳求,将JSON数据文件从云端恳求到本地渲染,其间对于JSON文件短少UUID的处理至关重要。
功用办法方面,咱们在ViewModel视图模型中创立了addCard增加身份卡办法替换了原有在View视图中完结的操作逻辑,进一步地,咱们创立了deleteItem删去身份卡办法,经过辨认当时卡片的UUID,指定卡片进行删去操作。
自此,Linkworld项目的功用越来越完善了,但还不够好。接下来的章节,咱们介绍另一种删去数据项的交互,以及修改页面、排序等功用的完结,请保持等待吧~
版权声明
本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究!