Nozioni di base su AWS

Creazione di un'applicazione iOS

Crea una semplice applicazione iOS utilizzando AWS Amplify

Modulo 5: aggiunta della possibilità di archiviare immagini

In questo modulo, aggiungerai storage e inserirai la possibilità di associare un'immagine alle note nell'app.

Introduzione

Ora che l'app delle note funziona, aggiungiamo la possibilità di associare un'immagine a ogni nota. In questo modulo, utilizzeremo l'interfaccia a riga di comando e le librerie Amplify per creare un servizio di storage grazie ad Amazon S3. Infine, aggiornerai l'applicazione iOS per abilitare le funzioni per caricare, recuperare ed effettuare il rendering dell'immagine.

Avrai modo di approfondire i seguenti aspetti

  • Creazione di un servizio di storage
  • Aggiornamento dell'applicazione iOS - La logica per caricare e scaricare immagini
  • Aggiornamento dell'applicazione iOS - L'interfaccia utente

Concetti chiave

Servizio di storage: l'archiviazione e le query per i file come le immagini e i video sono un requisito comune per la maggior parte delle applicazioni. Un'opzione per farlo è effettuare la codifica Base64 del file e inviarlo come stringa da salvare nel database. Ma questo procedimento ha anche degli svantaggi, ad esempio il fatto che il file codificato è più grande del binario originale, le operazioni sono costose a livello di codice e i procedimenti di codifica e decodifica adeguati sono ulteriormente complessi. Un'altra opzione è avere un servizio di storage specifico creato e ottimizzato per lo storage del file. I servizi di storage come Amazon S3 esistono per rendere il procedimento più facile, performante ed economico possibile.

 Tempo richiesto per il completamento

10 minuti

 Servizi utilizzati

Implementazione

  • Creazione del servizio di storage

    Per aggiungere una funzionalità di storage di immagini, utilizzeremo la categoria di storage Amplify:

    amplify add storage
    
    ? Please select from one of the below mentioned services: accept the default Content (Images, audio, video, etc.) and press enter
    ? Please provide a friendly name for your resource that will be used to label this category in the project: type image and press enter
    ? Please provide bucket name: accept the default and press enter
    ? Who should have access: accept the default Auth users only and press enter
    ? What kind of access do you want for Authenticated users? select all three options create/update, read, delete using the space and arrows keys, then press enter
    ? Do you want to add a Lambda Trigger for your S3 Bucket? accept the default No and press enter

    Dopo qualche istante, dovresti visualizzare quanto segue:

    Successfully added resource image locally
  • Distribuzione del servizio di storage

    Per distribuire il servizio di storage appena creato, apri il terminale ed esegui il comando:

    amplify push

    Premi Y per confermare e, dopo qualche istante, dovresti visualizzare quanto segue:

    ✔ Successfully pulled backend environment amplify from the cloud.
  • Aggiunta delle librerie di storage Amplify al progetto Xcode

    Prima di passare al codice, aggiungi la libreria di storage Amplify alle dipendenze del progetto. Apri il file Podfile e aggiungi la riga con AmplifyPlugins/AWSS3StoragePlugin 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
      pod 'AmplifyPlugins/AWSS3StoragePlugin', '~> 1.0'   # support for Amazon S3 storage
    
    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 AWSS3 (2.14.2)
    Installing AmplifyPlugins 1.0.4
    Generating Pods project
    Integrating client project
    Pod installation complete! There are 5 dependencies from the Podfile and 12 total pods installed.
  • Inizializzazione del plug-in di storage di Amplify al 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.add(plugin: AWSS3StoragePlugin())
       try Amplify.configure()
       print("Initialized Amplify");
    } catch {
       print("Could not initialize Amplify: \(error)")
    }
  • Aggiunta di metodi CRUD di immagini alla classe Backend

    Apri Backend.swift. Aggiungi i seguenti metodi in un punto qualsiasi della classe di back-end:

    // MARK: - Image Storage
    
    func storeImage(name: String, image: Data) {
    
    //        let options = StorageUploadDataRequest.Options(accessLevel: .private)
        let _ = Amplify.Storage.uploadData(key: name, data: image,// options: options,
            progressListener: { progress in
                // optionlly update a progress bar here
            }, resultListener: { event in
                switch event {
                case .success(let data):
                    print("Image upload completed: \(data)")
                case .failure(let storageError):
                    print("Image upload failed: \(storageError.errorDescription). \(storageError.recoverySuggestion)")
            }
        })
    }
    
    func retrieveImage(name: String, completed: @escaping (Data) -> Void) {
        let _ = Amplify.Storage.downloadData(key: name,
            progressListener: { progress in
                // in case you want to monitor progress
            }, resultListener: { (event) in
                switch event {
                case let .success(data):
                    print("Image \(name) loaded")
                    completed(data)
                case let .failure(storageError):
                    print("Can not download image: \(storageError.errorDescription). \(storageError.recoverySuggestion)")
                }
            }
        )
    }
    
    func deleteImage(name: String) {
        let _ = Amplify.Storage.remove(key: name,
            resultListener: { (event) in
                switch event {
                case let .success(data):
                    print("Image \(data) deleted")
                case let .failure(storageError):
                    print("Can not delete image: \(storageError.errorDescription). \(storageError.recoverySuggestion)")
                }
            }
        )
    }
  • Caricamento dell'immagine quando i dati vengono recuperati dall'API

    Ora che le funzioni di back-end sono disponibili, carichiamo le immagini quando ritorna la chiamata dell'API. Il punto centrale per l'aggiunta di questo comportamento è quando l'applicazione costruisce un'interfaccia utente delle note dai NoteData restituiti dall'API.

    Apri ContentView.swift e aggiorna l'inizializzatore della nota (aggiungi le righe da 8 a 17):

    // add a publishable's object property
    @Published var image : Image?
    
    // update init's code
    convenience init(from data: NoteData) {
        self.init(id: data.id, name: data.name, description: data.description, image: data.image)
    
        if let name = self.imageName {
            // asynchronously download the image
            Backend.shared.retrieveImage(name: name) { (data) in
                // update the UI on the main thread
                DispatchQueue.main.async() {
                    let uim = UIImage(data: data)
                    self.image = Image(uiImage: uim!)
                }
            }
        }
        // store API object for easy retrieval later
        self._data = data
    }

    Quando nell'istanza della nota è presente il nome di un'immagine, il codice chiama retrieveImage. Questa è una funzione asincrona. Quando l'immagine viene scaricata, serve una funzione da chiamare. La funzione crea un oggetto di interfaccia utente dell'immagine e lo assegna all'istanza della nota. Tieni presente che questa assegnazione avvia un aggiornamento dell'interfaccia utente e pertanto avviene nel thread principale dell'applicazione con DispatchQueue.main.async.

  • Aggiunta del codice dell'interfaccia utente per acquisire un'immagine

    Innanzitutto, aggiungiamo codice generico per supportare l'acquisizione dell'immagine. Questo codice può essere riutilizzato in varie applicazioni; mostra un selettore di immagini che consente all'utente di scegliere un'immagine dalla libreria di immagini.

    In Xcode, crea il nuovo file swift (⌘N, quindi seleziona Swift). Assegna il nome CaptureImageView.swift al file e aggiungi questo codice:

    import Foundation
    import UIKit
    import SwiftUI
    
    struct CaptureImageView {
    
      /// MARK: - Properties
      @Binding var isShown: Bool
      @Binding var image: UIImage?
    
      func makeCoordinator() -> Coordinator {
        return Coordinator(isShown: $isShown, image: $image)
      }
    }
    
    class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
      @Binding var isCoordinatorShown: Bool
      @Binding var imageInCoordinator: UIImage?
      init(isShown: Binding<Bool>, image: Binding<UIImage?>) {
        _isCoordinatorShown = isShown
        _imageInCoordinator = image
      }
      func imagePickerController(_ picker: UIImagePickerController,
                    didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
         guard let unwrapImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage else { return }
         imageInCoordinator = unwrapImage
         isCoordinatorShown = false
      }
      func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
         isCoordinatorShown = false
      }
    }
    
    extension CaptureImageView: UIViewControllerRepresentable {
        func makeUIViewController(context: UIViewControllerRepresentableContext<CaptureImageView>) -> UIImagePickerController {
            let picker = UIImagePickerController()
            picker.delegate = context.coordinator
    
            // picker.sourceType = .camera // on real devices, you can capture image from the camera
            // see https://medium.com/better-programming/how-to-pick-an-image-from-camera-or-photo-library-in-swiftui-a596a0a2ece
    
            return picker
        }
    
        func updateUIViewController(_ uiViewController: UIImagePickerController,
                                    context: UIViewControllerRepresentableContext<CaptureImageView>) {
    
        }
    }
  • Archiviazione delle immagini quando vengono create le note

    Richiama i metodi di storage dal back-end quando viene creata una nota. Apri ContentView.swift e modifica la AddNoteView per aggiungere un componente ImagePicker:

    // at the start of the Content View struct 
    @State var image : UIImage? // replace the previous declaration of image
    @State var showCaptureImageView = false
    
    // in the view, replace the existing PICTURE section
    Section(header: Text("PICTURE")) {
        VStack {
            Button(action: {
                self.showCaptureImageView.toggle()
            }) {
                Text("Choose photo")
            }.sheet(isPresented: $showCaptureImageView) {
                CaptureImageView(isShown: self.$showCaptureImageView, image: self.$image)
            }
            if (image != nil ) {
                HStack {
                    Spacer()
                    Image(uiImage: image!)
                        .resizable()
                        .frame(width: 250, height: 200)
                        .clipShape(Circle())
                        .overlay(Circle().stroke(Color.white, lineWidth: 4))
                        .shadow(radius: 10)
                    Spacer()
                }
            }
        }
    }

    Modifica la sezione Create Note (Crea nota) per archiviare l'immagine oltre che la nota:

    Section {
        Button(action: {
            self.isPresented = false
    
            let note = Note(id : UUID().uuidString,
                            name: self.$name.wrappedValue,
                            description: self.$description.wrappedValue)
    
            if let i = self.image  {
                note.imageName = UUID().uuidString
                note.image = Image(uiImage: i)
    
                // asynchronously store the image (and assume it will work)
                Backend.shared.storeImage(name: note.imageName!, image: (i.pngData())!)
            }
    
            // 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
            withAnimation { self.userData.notes.append(note) }
        }) {
            Text("Create this 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 con una nota. Utilizza nuovamente il segno + per creare una nota. Questa volta, aggiungi un'immagine selezionata dall'archivio immagini locale.

    Questo è il flusso completo.

    iOSTutorial-Module5-step1
    iOSTutorial-Module5-step2
    iOSTutorial-Module5-step3
    iOSTutorial-Module5-step4
    iOSTutorial-Module5-step5

Conclusione

Hai creato un'applicazione iOS utilizzando AWS Amplify! Hai inserito l'autenticazione nella tua app per permettere agli utenti di iscriversi, registrarsi e gestire gli account. L'app ha anche un'API GraphQL scalabile configurata con un database Amazon DynamoDB per permettere agli utenti di creare ed eliminare note. Inoltre, hai aggiunto anche lo storage dei file utilizzando Amazon S3 per permettere agli utenti di caricare le immagini e visualizzarle nella loro app.

Nell'ultima sezione troverai le istruzioni per riutilizzare o eliminare il back-end appena creato.

  • Condivisione del back-end tra più progetti

    Amplify semplifica la condivisione di un singolo back-end tra più applicazioni di front-end.

    In un terminale, accedi alla directory dell'altro progetto ed esegui il seguente comando:

    mkdir other-project
    cd other-project
    
    amplify pull
    
    ? Do you want to use an AWS profile? accept the default Yes and press enter
    ? Please choose the profile you want to use select the profile you want to use and press enter
    ? Which app are you working on? select the backend you want to share and press enter
    ? Choose your default editor: select you prefered text editor and press enter
    ? Choose the type of app that you're building select the operating system for your new project and press enter
    ? Do you plan on modifying this backend? most of the time, select No and press enter. All backend modifications can be done from the original iOS project.

    Dopo qualche secondo, vedrai il seguente messaggio:

    Added backend environment config object to your project.
    Run 'amplify pull' to sync upstream changes.

    Puoi visualizzare i due file di configurazione che sono stati estratti. Se rispondi 'Sì' quando ti viene richiesto se prevedi di modificare questo back-end, vedrai anche una directory Amplify

    ➜  other-project git:(master) ✗ ls -al
    total 24
    drwxr-xr-x   5 stormacq  admin   160 Jul 10 10:28 .
    drwxr-xr-x  19 stormacq  admin   608 Jul 10 10:27 ..
    -rw-r--r--   1 stormacq  admin   315 Jul 10 10:28 .gitignore
    -rw-r--r--   1 stormacq  admin  3421 Jul 10 10:28 amplifyconfiguration.json
    -rw-r--r--   1 stormacq  admin  1897 Jul 10 10:28 awsconfiguration.json
  • Eliminazione del back-end

    Quando crei un back-end per un test, un prototipo o solo a scopo di apprendimento, come quando segui questo tutorial, puoi eliminare le risorse cloud che sono state create.

    Anche se l'utilizzo di queste risorse nel contesto del tutorial rientra nel piano gratuito, è consigliabile eliminare le risorse inutilizzate nel cloud.

    Per la pulizia del progetto Amplify, in un terminale, esegui il seguente comando:

    amplify delete

    Dopo qualche istante, vedrai il messaggio in basso che conferma che tutte le risorse cloud sono state eliminate.

    ✔ Project deleted in the cloud
    Project deleted locally.

Grazie per aver seguito questo tutorial fino alla fine. Inviaci il tuo feedback utilizzando lo strumento in basso o effettuando una richiesta pull nel nostro repository Github.

Questo modulo è stato utile?

Grazie
Facci sapere cosa ti è piaciuto.
Chiudi
Spiacenti di non esserti stati d'aiuto
C'è qualcosa di obsoleto, ambiguo o approssimativo? Aiutaci a migliorare questo tutorial con il tuo feedback.
Chiudi

Complimenti!

Hai creato un'applicazione Web in AWS! Il passo successivo è approfondire le tecnologie specifiche di AWS e migliorare ulteriormente la tua applicazione.