Introducción a AWS
Crear una aplicación iOS
Crear una aplicación iOS sencilla con AWS Amplify

agregar almacenamiento
Módulo 5: agregar la capacidad de almacenar imágenes
En este módulo, agregará almacenamiento y la capacidad de asociar una imagen con las notas en su aplicación.
Introducción
Ahora que tenemos una aplicación de notas en funcionamiento, agreguemos la capacidad de asociar una imagen a cada nota. En este módulo, usará la CLI y las bibliotecas de Amplify para crear un servicio de almacenamiento con Amazon S3. Por último, actualizaremos la aplicación iOS para permitir la carga, la búsqueda y el procesamiento de imágenes.
Lo que aprenderá
- Crear un servicio de almacenamiento
- Actualice su aplicación IOS: la lógica para cargar y descargar imágenes
- Actualice su aplicación IOS: la interfaz de usuario
Conceptos clave
Servicio de almacenamiento: el almacenamiento y la consulta de archivos como imágenes y videos es un requisito común para la mayoría de las aplicaciones. Una opción para hacer esto es codificar el archivo con Base64 y enviarlo como cadena para guardarle en la base de datos. Esto viene con desventajas como que el archivo codificado es más grande que el archivo binario original, la operación es costosa en términos informáticos y la codificación y decodificación adecuadas agregan complejidad. Otra opción es crear y optimizar un servicio de almacenamiento específicamente para el almacenamiento de archivos. Los servicios de almacenamiento como Amazon S3 existen para aportar la mayor facilidad, eficiencia y rentabilidad posible.
Tiempo de realización
10 minutos
Servicios utilizados
Implementación
-
Crear el servicio de almacenamiento
Para agregar la funcionalidad de almacenamiento de imágenes, usaremos la categoría de almacenamiento 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
Luego de un momento, debería ver lo siguiente:
Successfully added resource image locally
-
Implementar el servicio de almacenamiento
Para implementar el servicio de almacenamiento que acabamos de crear, diríjase a su terminal y ejecute el comando:
amplify push
Presione Y para confirmar y, luego de un momento, debería ver lo siguiente:
✔ Successfully pulled backend environment amplify from the cloud.
-
Agregar bibliotecas de almacenamiento de Amplify al proyecto de Xcode
Antes de ir al código, agregue la biblioteca de almacenamiento de Amplify a las dependencias de su proyecto. Abra el archivo Podfile y agregue la línea con AmplifyPlugins/AWSS3StoragePlugin o copie/pegue el archivo completo a continuación.
# 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
En una terminal, ejecute el comando:
pod install
El comando tarda unos minutos en completarse. Debería ver esto (los números de versión reales pueden variar):
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.
-
Inicializar el complemento de almacenamiento de Amplify en tiempo de ejecución
En Xcode, abra Backend.swift y agregue una línea en la secuencia de inicialización de Amplify en el método init() privado. El bloque de código completo debería verse así:
// 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)") }
-
Agregar métodos de Image CRUD a la clase Backend
Abra Backend.swift. En cualquier lugar de la clase backend, agregue los siguientes métodos:
// 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)") } } ) }
-
Cargar imágenes cuando se recuperan datos de la API
Ahora que tenemos nuestras funciones de backend disponibles, carguemos las imágenes cuando regrese la llamada a la API. El lugar principal para agregar este comportamiento es cuando la aplicación crea una UI de Note a partir del NoteData devuelto por la API.
Abra ContentView.swift y actualice el inicializador de Note (agregue las líneas 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 }
Cuando el nombre de una imagen está presente en la instancia de Note, el código llama a retrieveImage. Esta es una función asincrónica. Se necesita una función para llamar cuando se descargue la imagen. La función crea un objeto de UI de Image y lo asigna a la instancia de Note. Tenga en cuenta que esta asignación desencadena una actualización de la interfaz de usuario, por lo que ocurre en el hilo principal de la aplicación con DispatchQueue.main.async.
-
Agregar código de UI para capturar una imagen
Primero, agregamos código genérico para admitir la captura de imágenes. Este código se puede reutilizar en muchas aplicaciones; muestra un selector de imágenes que permite al usuario elegir una imagen de su biblioteca de imágenes.
En Xcode, cree el nuevo archivo Swift (⌘N, luego seleccione Swift). Nombre el archivo CaptureImageView.swift y agregue este código:
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>) { } }
-
Almacenar imágenes cuando se crean notas
Invoquemos los métodos de almacenamiento de Backend cuando se crea una nota. Abra ContentView.swift y modifique AddNoteView para agregar 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() } } } }
Modifique la sección Create Note (Crear nota) para almacenar la imagen y 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") } }
-
Crear y probar
Para verificar que todo funcione como se espera, cree y ejecute el proyecto. Haga clic en el menú Product (Producto) y seleccione Run (Ejecutar) o presione ⌘R. No debería haber ningún error.
La aplicación comienza en la lista con una nota, si asume que su sesión aún sigue abierta. Use el signo + nuevamente para crear una nota. Esta vez, agregue una imagen seleccionada de la tienda de imágenes local.
Este es el flujo completo.
Conclusión
¡Ha creado una aplicación iOS con AWS Amplify! Ha agregado autenticación a su aplicación, lo que permite que los usuarios se registren, inicien sesión y administren su cuenta. La aplicación también cuenta con una API GraphQL escalable configurada con una base de datos Amazon DynamoDB que permite que los usuarios creen y eliminen notas. También ha agregado almacenamiento de archivos mediante Amazon S3, lo que permite que los usuarios carguen imágenes y las vean en su aplicación.
En la última sección, encontrará instrucciones para reutilizar o eliminar el backend que acabamos de crear.
-
Comparta su backend entre diferentes proyectos
Amplify facilita el intercambio de un solo backend entre diferentes aplicaciones de frontend.
En una terminal, diríjase hasta el otro directorio de su proyecto y ejecute el siguiente 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.
Luego de unos segundos, verá el siguiente mensaje:
Added backend environment config object to your project. Run 'amplify pull' to sync upstream changes.
Puede ver los dos archivos de configuración que se han extraído. Cuando responda “Sí” a la pregunta “¿Planea modificar este backend?”, también verá un directorio de 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
-
Eliminar el backend
Cuando crea un backend para una prueba o un prototipo, o simplemente con fines de aprendizaje, al igual que cuando sigue este tutorial, luego desea eliminar los recursos en la nube que se han creado.
Aunque el uso de estos recursos en el contexto de este tutorial se incluye en la capa gratuita, es una buena práctica limpiar los recursos que no se utilizan en la nube.
Para limpiar su proyecto de Amplify, en una terminal, ejecute el siguiente comando:
amplify delete
Luego de un momento, verá el siguiente mensaje que confirma que se han eliminado todos los recursos de la nube.
✔ Project deleted in the cloud Project deleted locally.
Gracias por haber seguido este tutorial hasta el final. Háganos saber sus comentarios con la siguiente herramienta o una solicitud de extracción en nuestro repositorio de Github.
¿Este módulo le resultó útil?
¡Felicitaciones!
¡Creó con éxito una aplicación Web en AWS! Como paso siguiente, profundice aún más en las tecnologías específicas de AWS y lleve su aplicación al siguiente nivel.