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.
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.
Complimenti!
Hai creato un'applicazione Web in AWS! Il passo successivo è approfondire le tecnologie specifiche di AWS e migliorare ulteriormente la tua applicazione.