Nozioni di base su AWS
Creazione di un'applicazione iOS
Crea una semplice applicazione iOS utilizzando AWS Amplify

aggiungi api e database
Modulo 4: Aggiunta di un'API GraphQL e di un database
In questo modulo verrà utilizzata l'interfaccia a riga di comando (CLI) di Amplify per configurare e aggiungere un'API GraphQL alla tua app.
Introduzione
Ora che abbiamo creato e configurato l'applicazione con l'autenticazione utente, aggiungiamo un'API e le operazioni CRUD (Crea, Leggi, Aggiorna, Elimina) in un database.
In questo modulo aggiungerai un'API alla nostra app usando l'interfaccia a riga di comando (CLI) e le librerie di Amplify. L'API che creerai è un'API GraphQL che sfrutta AWS AppSync (un servizio GraphQL gestito) supportato da Amazon DynamoDB (un database NoSQL). Per un'introduzione a GraphQL, visita questa pagina.
L'applicazione che creerai sarà un'app di note che consentirà agli utenti di creare, eliminare ed elencare note. Questo esempio ti darà un'idea su come creare molti tipi famosi di applicazioni CRUD+L (crea, leggi, aggiorna, elimina ed elenca).
Avrai modo di approfondire i seguenti aspetti
- Creazione e distribuzione di un'API GraphQL
- Scrittura di un codice front-end per interagire con l'API
Concetti chiave
API: fornisce un'interfaccia di programmazione che consente la comunicazione e le interazioni tra più intermediari software.
GraphQL: un linguaggio di query e un'implementazione API lato server basati su una rappresentazione tipizzata dell'applicazione. Questa rappresentazione API viene dichiarata usando uno schema basato sul tipo di sistema GraphQL. (Per ulteriori informazioni su GraphQL, visita questa pagina.)
Tempo richiesto per il completamento
20 minuti
Servizi utilizzati
Implementazione
-
Creazione di un servizio API GraphQL e di un database
Per creare l'API GraphQL e il relativo database di supporto, apri un terminale ed esegui questo comando dalla directory del progetto:
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
L'editor di testo predefinito che hai selezionato quando hai inizializzato il progetto (Amplify init) apre uno schema di dati precompilato.
Elimina lo schema e sostituiscilo con lo schema GraphQL della nostra applicazione:
type NoteData @model @auth (rules: [ { allow: owner } ]) { id: ID! name: String! description: String image: String }
Il modello di dati è composto da una classe NoteData e da quattro proprietà: ID e nome sono obbligatorie, descrizione e immagine sono stringhe facoltative.
Il Transformer @model indica che vogliamo creare un database per archiviarvi questi dati.
Il Trasformer @auth aggiunge le regole di autenticazione per consentire l'accesso a questi dati. Per questo progetto, vogliamo che l'accesso sia consentito solo al proprietario dei NoteData.
Una volta completato, non dimenticarti di salvare.
-
Generazione del codice lato client
Sulla base della definizione del modello di dati GraphQL appena creato, Amplify genera un codice lato client (ossia codice Swift) per rappresentare i dati nella nostra applicazione.
Per generare il codice, esegui il comando seguente nel tuo terminale:
amplify codegen models
Questo crea file Swift nella directory amplify/generated/models, come puoi vedere con:
➜ 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
Importa questi file nel progetto Xcode: individuali nella pagina di ricerca e trascinali e rilasciali nel progetto in Xcode.
-
Distribuzione del database e del servizio API
Per distribuire il database e l'API di back-end appena creati, apri il terminale ed esegui il comando:
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
Dopo alcuni minuti, dovresti visualizzare un messaggio di corretta esecuzione:
✔ All resources are updated in the cloud GraphQL endpoint: https://yourid.appsync-api.eu-central-1.amazonaws.com/graphql
-
Aggiunta della libreria client API al progetto Xcode
Prima di passare al codice, aggiungi la libreria delle API Amplify alle dipendenze del progetto. Apri il file Podfile e aggiungi la riga con AmplifyPlugins/AWSAPIPlugin oppure copia e incolla l'intero file sotto.
# 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
In un terminale, esegui il comando:
pod install
Il completamento del comando richiede alcuni istanti. Dovresti vedere quello che segue (il numero effettivo della versione potrebbe variare):
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.
-
Inizializzazione delle librerie Amplify al momento del runtime
Torna a Xcode, apri Backend.swift e aggiungi una riga alla sequenza di inizializzazione Amplify nel metodo private init(). Il blocco di codice completo dovrebbe avere il seguente aspetto:
// 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)") }
-
Aggiunta del ponte tra il modello di dati GraphQL e il modello dell'applicazione
Il nostro progetto contiene già un modello di dati per rappresentare una nota. Quindi ho deciso di continuare a utilizzare quel modello e di fornire un modo facile per convertire una NoteData in una nota. Apri ContentView.swift e aggiungi questo inizializzatore alla classe Note.
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! }
-
Aggiunta di metodi CRUD API alla classe Backend
Aggiungiamo tre metodi per chiamare la nostra API: un metodo per eseguire una query della nota, un metodo per creare una nuova nota e un metodo per eliminare una nota. Tieni presente che questi metodi funzionano nel modello di dati dell'applicazione (Note) per semplificare l'interazione dall'interfaccia utente. Questi metodi convertono la nota in oggetti NoteData di GraphQL in modo trasparente.
Apri il file Backend.swift e aggiungi il seguente frammento alla fine della classe Backend:
// 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)") } } }
Nello stesso file Backend.swift, aggiorna il metodo updateUserData(withSignInStatus:) perché abbia il seguente aspetto:
// 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 = [] } } }
Ora, non ci rimane che creare un pezzo dell'interfaccia utente per creare una nuova nota e per eliminare una nota dall'elenco.
-
Aggiunta di un pulsante Edit (Modifica) per aggiungere una nota
Ora che i pezzi del back-end e del modello di dati sono sistemati, l'ultima fase in questa sezione è consentire agli utenti di creare una nuova nota e di eliminarli.
In Xcode, apri ContentView.swift
a. Nella struttura ContentView, aggiungi le variabili di stato collegate all'interfaccia utente.
// 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. Aggiungi una struttura View in un punto qualsiasi del file per consentire all'utente di creare una nuova nota:
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. Aggiungi un pulsante + nella barra di navigazione per visualizzare un foglio per la creazione di una nota
Torna alla struttura ContentView e sostituisci navigationBarItems(leading SignOutButton()) con
.navigationBarItems(leading: SignOutButton(), trailing: Button(action: { self.showCreateNote.toggle() }) { Image(systemName: "plus") }) }.sheet(isPresented: $showCreateNote) { AddNoteView(isPresented: self.$showCreateNote, userData: self.userData)
-
Aggiunta del comportamento Scorri per eliminare
Infine, nella ContentView, aggiungi il comportamento 'scorri per eliminare': aggiungi il metodo .onDelete { } alla struttura 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) } }
-
Creazione e test
Per verificare che tutto funzioni come previsto, crea ed esegui il progetto. Fai clic sul menu Product (Prodotto) e seleziona Run (Esegui) o digita ⌘R. Non dovrebbero essere restituiti errori.
Supponendo che non ti sia ancora disconnesso, l'applicazione inizia nell'elenco vuoto. Ora è presente un pulsante + per aggiungere una nota. Tocca il segno +, quindi Create this Note (Crea questa nota): la nota dovrebbe comparire nell'elenco.
Puoi chiudere la AddNoteView trascinandola verso il basso. Tieni presente che, nel simulatore iOS, non è possibile toccare + una seconda volta, ma devi innanzitutto "trascinare per aggiornare" l'elenco.
Puoi eliminare la nota facendo scorrere la riga verso sinistra.
Questo è il flusso completo.
Conclusione
Hai creato un'app iOS! Usando AWS Amplify hai aggiunto un'API GraphQL e hai configurato le funzionalità Crea, Leggi ed Elimina nella tua applicazione.
Nel prossimo modulo, aggiungeremo l'interfaccia utente e il comportamento per gestire le immagini.