开始使用 AWS
构建 iOS 应用程序
使用 AWS Amplify 创建简单的 iOS 应用程序
模块 4:添加 GraphQL API 和数据库
在此模块中,您将使用 Amplify CLI 和库配置和添加 GraphQL API 到您的应用程序中。
简介
现在,我们已经创建并配置了带用户身份验证功能的应用程序。接下来,我们要在数据库中添加 API 以及“创建”、“读取”、“更新”、“删除”(CRUD) 操作。
在此模块中,您将使用 Amplify CLI 和库将 API 添加到您的应用程序中。您将创建的 API 是 GraphQL API,它利用 Amazon DynamoDB(NoSQL 数据库)支持的 AWS AppSync(托管 GraphQL 服务)。有关 GraphQL 的介绍,请访问此页面。
您将构建的应用程序是备注记录应用程序,用户可使用它创建、删除和列出备注。本示例将让您了解如何构建很多常见类型的 CRUD+L(创建、读取、更新、删除和列出)应用程序。
您将学到的内容
- 创建和部署 GraphQL API
- 编写前端代码以与 API 互动
重要概念
API – 提供编程接口,以允许多个软件中介之间的通信和互动。
GraphQL – 基于应用程序类型化表示的查询语言和服务器端 API 实施。此 API 表示使用基于 GraphQL 类型系统的架构声明。(要了解有关 GraphQL 的更多信息,请访问此页面。)
完成时间
20 分钟
使用的服务
实施
-
创建 GraphQL API 服务和数据库
要创建 GraphQL API 及其后备数据库,请打开一个终端,然后从项目目录中执行此命令:
amplify add api ? Please select from one of the below mentioned services: select GraphQL and press enter ? Provide API name: select the default, press enter ? Choose the default authorization type for the API: use the arrow key to select Amazon Cognito User Pool and press enter ? Do you want to configure advanced settings for the GraphQL API: select the default No, I am done and press enter ? Do you have an annotated GraphQL schema? keep the default N and press enter ? What best describes your project: choose any model, we are going to replace it with our own anyway. Press enter ? Do you want to edit the schema now? type Y and press enter
初始化项目时选择的默认文本编辑器 (amplify init) 将使用预构建数据 schema 打开。
删除此 schema,并使用我们的应用程序 GraphQL schema 替换:
type NoteData @model @auth (rules: [ { allow: owner } ]) { id: ID! name: String! description: String image: String }
数据模型由一个类 NoteData 和 4 个属性组成:ID 和名称是必填项,描述和图像是选填字符串。
@model 转换器指示我们要创建数据库来存储数据。
@auth 转换器添加了身份验证规则,以允许访问这些数据。对于此项目,我们希望仅 NoteData 的拥有者有访问它们的权限。
完成后,请不要忘记保存。
-
生成客户端代码
根据我们刚刚创建的 GraphQL 数据模型定义,Amplify 会生成客户端代码(即 Swift 代码)以代表我们应用程序中的数据。
要生成代码,请在终端执行以下命令:
amplify codegen models
此操作将在 amplify/generated/models 目录创建 Swift 文件。您可以通过以下方式看到这些内容:
➜ iOS Getting Started git:(master) ✗ ls -al amplify/generated/models total 24 drwxr-xr-x 5 stormacq admin 160 Jul 9 14:20 . drwxr-xr-x 3 stormacq admin 96 Jul 9 14:20 .. -rw-r--r-- 1 stormacq admin 380 Jul 9 14:20 AmplifyModels.swift -rw-r--r-- 1 stormacq admin 822 Jul 9 14:20 NoteData+Schema.swift -rw-r--r-- 1 stormacq admin 445 Jul 9 14:20 NoteData.swift
在 Xcode 项目中导入这些文件:在查找器中找到它们,然后将其拖放到 Xcode 中的项目。
-
部署 API 服务和数据库
要部署我们刚刚创建的后端 API 和数据库,请转至您的终端,然后执行命令:
amplify push # press Y when asked to continue ? Are you sure you want to continue? accept the default Y and press enter ? Do you want to generate code for your newly created GraphQL API type N and press enter
几分钟后,您应该会看到一条成功消息:
✔ All resources are updated in the cloud GraphQL endpoint: https://yourid.appsync-api.eu-central-1.amazonaws.com/graphql
-
将 API 客户端库添加到 Xcode 项目
在转至代码之前,请将 Amplify API 库添加到项目的依赖项中。打开 Podfile 文件,然后使用 AmplifyPlugins/AWSAPIPlugin 添加行,或者复制并粘贴以下整个文件。
# you need at least version 13.0 for this tutorial, more recent versions are valid too platform :ios, '13.0' target 'getting started' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! # Pods for getting started pod 'Amplify', '~> 1.0' # required amplify dependency pod 'Amplify/Tools', '~> 1.0' # allows to call amplify CLI from within Xcode pod 'AmplifyPlugins/AWSCognitoAuthPlugin', '~> 1.0' # support for Cognito user authentication pod 'AmplifyPlugins/AWSAPIPlugin', '~> 1.0' # support for GraphQL API end
在终端执行以下命令:
pod install
该命令需要一些时间才能完成。您将看到(实际版本号可能有所不同):
Analyzing dependencies Downloading dependencies Installing AmplifyPlugins 1.0.4 Installing AppSyncRealTimeClient (1.1.6) Installing ReachabilitySwift (5.0.0) Installing Starscream (3.0.6) Generating Pods project Integrating client project Pod installation complete! There are 4 dependencies from the Podfile and 11 total pods installed.
-
在运行时初始化 Amplify 库
返回到 Xcode,打开 Backend.swift,然后在私有 init() 方法的 Amplify 初始化序列中添加一行。完整代码块应如下所示:
// initialize amplify do { try Amplify.add(plugin: AWSCognitoAuthPlugin()) try Amplify.add(plugin: AWSAPIPlugin(modelRegistration: AmplifyModels())) try Amplify.configure() print("Initialized Amplify") } catch { print("Could not initialize Amplify: \(error)") }
-
在 GraphQL 数据模型和应用程序模型之间添加桥接
我们的项目已经有一个数据模型来表示备注。因此,我决定继续使用该模型,并提供一种将 NoteData 转换为备注的简单方法。打开 ContentView.swift 并将此初始化程序添加到备注类中。
convenience init(from data: NoteData) { self.init(id: data.id, name: data.name, description: data.description, image: data.image) // store API object for easy retrieval later self._data = data } fileprivate var _data : NoteData? // access the privately stored NoteData or build one if we don't have one. var data : NoteData { if (_data == nil) { _data = NoteData(id: self.id, name: self.name, description: self.description, image: self.imageName) } return _data! }
-
将 API CRUD 方法添加到后端类
我们添加 3 种方法来调用 API:一种查询 Note 的方法,一种创建新 Note 的方法,以及一种删除 Note 的方法。请注意,这些方法适用于应用程序数据模型(备注),以便从用户界面轻松交互。这些方法可透明地将 Note 转换为 GraphQL 的 NoteData 对象。
打开 Backend.swift 文件,然后在后端类末尾添加以下 snipet:
// MARK: API Access func queryNotes() { _ = Amplify.API.query(request: .list(NoteData.self)) { event in switch event { case .success(let result): switch result { case .success(let notesData): print("Successfully retrieved list of Notes") // convert an array of NoteData to an array of Note class instances for n in notesData { let note = Note.init(from: n) DispatchQueue.main.async() { UserData.shared.notes.append(note) } } case .failure(let error): print("Can not retrieve result : error \(error.errorDescription)") } case .failure(let error): print("Can not retrieve Notes : error \(error)") } } } func createNote(note: Note) { // use note.data to access the NoteData instance _ = Amplify.API.mutate(request: .create(note.data)) { event in switch event { case .success(let result): switch result { case .success(let data): print("Successfully created note: \(data)") case .failure(let error): print("Got failed result with \(error.errorDescription)") } case .failure(let error): print("Got failed event with error \(error)") } } } func deleteNote(note: Note) { // use note.data to access the NoteData instance _ = Amplify.API.mutate(request: .delete(note.data)) { event in switch event { case .success(let result): switch result { case .success(let data): print("Successfully deleted note: \(data)") case .failure(let error): print("Got failed result with \(error.errorDescription)") } case .failure(let error): print("Got failed event with error \(error)") } } }
在同一 Backend.swift 文件中,更新 updateUserData(withSignInStatus:) 方法如下所示:
// change our internal state, this triggers an UI update on the main thread func updateUserData(withSignInStatus status : Bool) { DispatchQueue.main.async() { let userData : UserData = .shared userData.isSignedIn = status // when user is signed in, query the database, otherwise empty our model if status { self.queryNotes() } else { userData.notes = [] } } }
现在,只需创建一个用户界面即可创建新 Note 和从列表中删除 Note。
-
添加“Edit”按钮以添加备注
现在,后端和数据模型已到位,本节的最后一步是让用户创建新的 Note 然后将其删除。
在 Xcode 中,打开 ContentView.swift
a.在 ContentView 结构中,添加绑定到用户界面的状态变量。
// add at the begining of ContentView class @State var showCreateNote = false @State var name : String = "New Note" @State var description : String = "This is a new note" @State var image : String = "image"
b.在文件中的任何位置,添加视图结构,以允许用户创建新的备注:
struct AddNoteView: View { @Binding var isPresented: Bool var userData: UserData @State var name : String = "New Note" @State var description : String = "This is a new note" @State var image : String = "image" var body: some View { Form { Section(header: Text("TEXT")) { TextField("Name", text: $name) TextField("Name", text: $description) } Section(header: Text("PICTURE")) { TextField("Name", text: $image) } Section { Button(action: { self.isPresented = false let noteData = NoteData(id : UUID().uuidString, name: self.$name.wrappedValue, description: self.$description.wrappedValue) let note = Note(from: noteData) // asynchronously store the note (and assume it will succeed) Backend.shared.createNote(note: note) // add the new note in our userdata, this will refresh UI self.userData.notes.append(note) }) { Text("Create this note") } } } } }
c.在导航栏上添加“+”按钮演示数据表以创建备注
返回 ContentView 结构,将 navigationBarItems(leading SignOutButton()) 替换为:
.navigationBarItems(leading: SignOutButton(), trailing: Button(action: { self.showCreateNote.toggle() }) { Image(systemName: "plus") }) }.sheet(isPresented: $showCreateNote) { AddNoteView(isPresented: self.$showCreateNote, userData: self.userData)
-
添加“滑动删除”行为
最后,在 ContentView 中添加“轻扫以删除”行为:将 .onDelete { } 方法添加到 ForEach 结构中:
ForEach(userData.notes) { note in ListRow(note: note) }.onDelete { indices in indices.forEach { // removing from user data will refresh UI let note = self.userData.notes.remove(at: $0) // asynchronously remove from database Backend.shared.deleteNote(note: note) } }
-
构建和测试
要验证一切是否都按预期运行,请构建并运行项目。单击产品菜单,然后选择运行或键入 ⌘R。应该不会出现错误。
假设您仍然登录,该应用程序一开始会提供一个空列表。它现在有一个“+”按钮可用于添加备注。点击“+”符号,点击“创建此备注”,备注将出现在列表中。
您可以向下拉来关闭 AddNoteView。请注意,在 iOS 模拟器上,无法再次点击“+”,您需要先“拉取以刷新”列表。
您可以通过向左滑动一行来删除 Note。
以下是完整流程。