Nozioni di base su AWS

Creazione di un'applicazione iOS

Crea una semplice applicazione iOS utilizzando AWS Amplify

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

  • 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.

  • 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.

  • 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
  • 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.
  • 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)")
    }
  • 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!
    }
  • 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.

  • 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)
  • 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)
        }
    }
  • 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.

Questo modulo è stato utile?

Aggiungi storage