项目档案

SwiftUI中目录app的在线版本

单击上面的按钮下载下一个示例项目的代码。这个项目是Directory赢博体育程序的在线版本。这个版本的赢博体育程序与一个存储赢博体育使用该赢博体育程序的用户的赢博体育目录条目的web服务通信。

网关类

本地存储数据的赢博体育程序通常使用一个模型类来管理存储和检索赢博体育程序的数据。与后端赢博体育程序的模型类等价的是一个网关类。下面是我为这个赢博体育构建的网关类的概要:

类网关:ObservableObject {@Published var人:[人]= []var关键:字符串= " func分类列出(名称n:字符串、密码p:字符串)异步- > Bool{/ /代码来创建一个新用户会在这里}func登录(名称n:字符串、密码p:字符串)异步- > Bool{/ /代码登录一个文物的用户会在这里}func()异步刷新{这里获取我们的目录条目/ /代码}func addPerson (_ p:Person) async{//向目录添加新Person的代码到这里}func removePerson(at n: Int) async{//删除目录条目的代码到这里}}

该类有两个重要属性:用户的密钥和Person对象列表。密钥将在成功登录或创建新用户后设置。refresh()方法将向服务器发送一个请求,以获取用户的目录条目列表。该查询的结果将存储在people数组中,我们将其发布到赢博体育程序的接口。

注意,refresh()、addPerson()和removePerson()方法都被标记为async。这意味着我们必须小心地只在任务内部调用这些方法。

处理POST和DELETE

在SwiftUI天气赢博体育中,我展示了一些如何使用async和await来管理GET请求的例子。对于这个赢博体育程序,我们还需要处理POST操作,将新的Person对象发布到用户目录,并处理DELETE操作来删除Person对象。

下面是我们网关的addPerson()方法的代码:

函数addPerson(_ p: Person) async{守卫!键。isEmpty else {return} let urlStr = "https://cmsc106.net/directory/people/\(key)" guard let url = url (string: urlStr) else {return} var request = URLRequest(url: url) request。httpMethod = "POST"请求。setValue("application/json", forHTTPHeaderField: "Content-Type") do {let jsonData = try JSONEncoder().encode(p) let (_, response) = try await URLSession.shared。上传(for: request, from: jsonData) guard(响应为!)HTTPURLResponse)。statusCode == 200 else {return} await refresh()} catch {}}

GET和POST请求之间的主要区别在于GET请求使用URLSession.shared.data()方法,而POST请求使用URLSession.shared.upload()方法。data()方法接受一个URL对象作为参数,而upload()方法同时接受一个URLRequest对象和要发布的数据作为参数。URLRequest对象包含请求URL以及将其指定为POST请求(其主体包含JSON代码)的附加信息。

removePerson()向服务器发送一个DELETE请求:

函数removePerson(at n: Int) async{守卫!键。isEmpty else {return} let toRemove = people[n] let urlStr = "https://cmsc106.net/directory/people/\(key)/\(toRemove.id)" guard let url = url (string: urlStr) else {return} var request = URLRequest(url: url) request。httpMethod = "DELETE" do {let (_, response) = try await URLSession.shared。数据(for:请求)守卫(响应为!)HTTPURLResponse)。statusCode == 200 else {return} await refresh()} catch {}}

与GET请求一样,DELETE请求也使用URLSession.shared.data()。两者之间的区别在于,对于GET请求,我们将向data()方法传递一个URL对象,而对于DELETE请求,我们将传递一个包含URL并将其标识为DELETE请求的URLRequest对象。

与网关交互

由于该赢博体育与后端交互,后端要求用户在使用该目录之前登录,因此赢博体育中的第一个视图是登录视图。下面是第一个视图的代码:

struct LoginView: View {@StateObject var gateway:gateway @State var goToMain = false @State var user: String = "" @State var password: String = "" var body: some View {NavigationView {VStack {NavigationLink(destination: DirectoryView(gateway:gateway), isActive: $goToMain) {EmptyView()} HStack {Text(" user:").frame(宽度:100,高度:20,对齐:.trailing) TextField("",text:$user). textfieldstyle (. roundedborder). disableautocorrection (true)} HStack {text ("Password:").frame(width:100,height:20,alignment: .trailing) SecureField("",text:$ Password). textfieldstyle (. roundedborder)} HStack {Button("New user "){Task {goToMain = await gateway. text = "Password ")。newUser(name: user, password: password)}}. buttonstyle (. borderedprominent).padding() Spacer() Button(" login "){Task {goToMain =等待网关。. login(name: user, password: password)}}.buttonStyle(.borderedProminent).padding()}}}}}

这个登录视图使用了一个NavigationLink来导航到赢博体育中的主视图。在这个例子中,NavigationLink是一个不可见的链接,它使用goToMain状态变量来触发导航。

Log In和New User按钮的动作闭包都包含处理登录过程的任务。由于网关newUser()和login()方法都被标记为异步,因此在任务中运行它们时需要小心。这两个方法都将返回一个布尔值,以指示它们是否成功。在本例中,我们将布尔值结果赋值给goToMain属性:在成功登录的情况下,这将触发导航到赢博体育程序的主视图。(login()方法还将调用网关的refresh()方法来在成功登录时获取用户的目录项。