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 中的專案。

    iOSTutorial-Module4-Step1
  • 部署 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' (拉動以重新整理) 清單。

    您可以向左滑動以刪除附註。

    這就是完整的流程。

    iOSTutorial-Module4-step2
    iOSTutorial-Module4-step3
    iOSTutorial-Module4-step4
    iOSTutorial-Module4-step5

結論

您現在已經建立了一個 iOS 應用程式! 透過使用 AWS Amplify,您為您的應用程式新增了 GraphQL API,並設定了建立、讀取和刪除功能。

在下一個單元中,我們將新 UI 和行為來管理圖片。

這個單元對您是否有幫助?

感謝您
請告訴我們您喜歡的部分。
關閉
抱歉,讓您失望
是有內容過時、令人困擾,或不準確嗎? 請提供意見回饋,協助我們改進此教學課程。
關閉

新增儲存體