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) 時,選取的預設文字編輯器將以預先建置的資料結構描述開啟。
刪除結構描述,並用我們的應用程式 GraphQL 結構描述將其取代:
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,然後在 private 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 資料模型與 App 模型間新增橋接
我們的專案已有一個資料模型來表示附註。因此,我做出了一個設計決策,以繼續使用該模型,並提供一種將 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:一種用於查詢附註,一種用於建立新的附註,以及一種用於刪除附註。請注意,這些方法適用於應用程式資料模型 (附註),以簡化與使用者界面的互動。這些方法將附註以透明的方式轉換為 GraphQL 的 NoteData 物件。
開啟 Backend.swift 檔案,然後在後端類別的末尾新增以下程式碼片段:
// 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 = [] } } }
現在,剩下的就是建立使用者界面,以建立新的附註並從清單中刪除附註。
-
新增編輯按鈕以新增附註
現在已經設置後端和資料模型區塊,這部分的最後一步是允許使用者建立新的附註以及將其刪除。
在 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 中,新增 'swipe to delete' (輕掃以刪除) 行為:將 .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) } }
-
建置和測試
若要驗證是否一切如預期,請建置並執行專案。按一下 Product (產品) 功能表,然後選取 Run (執行) 或輸入 ⌘R。應不會發生錯誤。
假設您仍處於登入狀態,該應用程式將從空白清單開始。其現在有一個 + 按鈕,可用於新增附註。點按 + 號,點按 Create this Note (建立此附註),該註釋應出現在清單中。
您可以透過下拉來關閉 AddNoteView。請注意,在 iOS 模擬器上,無法再次點按 +,您需要先 'pull-to-refresh' (拉動以重新整理) 清單。
您可以向左滑動以刪除附註。
這就是完整的流程。