Conceitos básicos da AWS
Criar uma aplicação iOS
Criar uma aplicação iOS simples usando o AWS Amplify

adicionar armazenamento
Módulo 5: Adicionar a capacidade de armazenar imagens
Neste módulo, você adicionará armazenamento e a capacidade de associar imagens às observações no aplicativo.
Introdução
Agora que o aplicativo de notas está funcionando, vamos adicionar a capacidade de associar uma imagem a cada nota. Neste módulo, você usará a CLI e as bibliotecas do Amplify para criar um serviço de armazenamento usando o Amazon S3. Para terminar, você atualizará o aplicativo iOS para habilitar o upload, a busca e a renderização de imagens.
O que você aprenderá
- Criar um serviço de armazenamento
- Atualizar o aplicativo iOS - a lógica para fazer upload e download de imagens
- Atualizar o aplicativo iOS - a interface do usuário
Principais conceitos
Serviço de armazenamento - Armazenamento e consulta de arquivos, como imagens e vídeos, é um requisito comum para a maioria das aplicações. Uma opção para fazer isso é codificar o arquivo em Base64 e enviar como uma string para salvar no banco de dados. Essa opção apresenta desvantagens, como o arquivo codificado ser maior que o binário original, a operação ser computacionalmente cara e a complexidade adicional devido à codificação e decodificação adequadas. Outra opção seria ter um serviço de armazenamento especificamente criado e otimizado para o armazenamento de arquivos. Serviços de armazenamento como o Amazon S3 existem para tornar isso o mais fácil, eficiente e barato possível.
Tempo para a conclusão
10 minutos
Serviços usados
Implementação
-
Criar o serviço de armazenamento
Para adicionar a funcionalidade de armazenamento de imagens, usaremos a categoria de armazenamento 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
Após algum tempo, você deverá ver:
Successfully added resource image locally
-
Implantar o serviço de armazenamento
Para implantar o serviço de armazenamento que acabamos de criar, acesse o seu terminal e execute o comando:
amplify push
Pressione Y para confirmar e, em breve, deverá aparecer:
✔ Successfully pulled backend environment amplify from the cloud.
-
Adicionar bibliotecas de armazenamento do Amplify ao projeto Xcode
Antes de acessar o código, você adiciona a biblioteca de armazenamento do Amplify às dependências do projeto. Abra o arquivo Podfile e adicione a linha com AmplifyPlugins/AWSS3StoragePlugin ou copie/cole o arquivo inteiro abaixo.
# 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
Em um terminal, execute o comando:
pod install
O comando leva alguns minutos para ser concluído. Você deve ver isto (os números da versão real podem 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 o plugin de armazenamento do Amplify em tempo de execução
De volta ao Xcode, abra o Backend.swift e adicione uma linha na sequência de inicialização do Amplify no método privado init(). Aparência do bloco de códigos completo:
// 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)") }
-
Adicionar métodos de CRUD de imagem à classe Backend
Abra Backend.swift. Em qualquer lugar da classe de back-end, adicione os seguintes 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)") } } ) }
-
Carregar imagem quando dados são recuperados da API
Com as funções back-end disponíveis, vamos carregar as imagens quando a chamada de API retornar. O local central para adicionar esse comportamento é quando o aplicativo cria uma interface de usuário de nota a partir do NoteData retornado pela API.
Abra ContentView.swift e atualize o inicializador de Nota (adicione as linhas 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 um nome de imagem está presente na instância de Nota, o código chama retrieveImage. Essa é uma função assíncrona. É necessária uma função para a chamada quando a imagem é obtida por download. A função cria o objeto interface de usuário da imagem e o atribui à instância de Nota. Observe que essa atribuição dispara uma atualização da interface de usuário. Isso ocorre no thread principal da aplicação com DispatchQueue.main.async.
-
Adicionar código de IU para capturar uma imagem
Primeiro, adicionamos um código genérico para dar suporte à captura de imagem. Este código pode ser reutilizado em muitas aplicações; ele mostra um seletor de imagem, permitindo que o usuário escolha uma imagem na biblioteca de imagens.
No Xcode, crie no novo arquivo swift (⌘N e, depois, selecione Swift). Nomeie o arquivo como CaptureImageView.swift e adicione 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>) { } }
-
Armazenar uma imagem quando notas forem criadas
Vamos invocar os métodos de armazenamento do back-end quando uma nota for criada. Abra ContentView.swift e modifique o AddNoteView para adicionar um 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 a seção Criar nota para armazenar a imagem e a 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") } }
-
Compilar e testar
Para verificar se tudo está funcionando conforme o esperado, compile e execute o projeto. Clique no menu Product (Produto) e selecione Run (Executar)ou digite ⌘R. Não deve aparecer nenhum erro.
Supondo que você ainda esteja conectado, o aplicativo inicia a lista com uma nota. Use o sinal + novamente para criar uma nota. Desta vez, adicione uma imagem selecionada do armazenamento de imagens locais.
Veja aqui o fluxo completo.
Conclusão
Você criou uma aplicação iOS usando o AWS Amplify! Você adicionou autenticação ao seu aplicativo, permitindo que os usuários se cadastrem, façam login e gerenciem suas contas. O aplicativo também tem uma API GraphQL escalonável configurada com um banco de dados Amazon DynamoDB, permitindo aos usuários criar e excluir notas. Você também adicionou armazenamento de arquivos usando o Amazon S3, permitindo aos usuários fazer upload de imagens e visualizá-las nos aplicativos deles.
Na última seção, você encontrará instruções para reutilizar ou excluir back-end recém-criado.
-
Compartilhar o back-end entre vários projetos
O Amplify facilita o compartilhamento de um único back-end entre várias aplicações front end.
Em um terminal, navegue para seu outro diretório de projeto e execute o seguinte 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.
Após alguns segundos, você verá a seguinte mensagem:
Added backend environment config object to your project. Run 'amplify pull' to sync upstream changes.
Note os dois arquivos de configurações que foram extraídos. Ao responder 'Sim' para a pergunta 'Pretende modificar este back-end?', você também vê um diretório do 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
-
Configurar o back-end
Ao criar um back-end para um teste ou protótipo, ou apenas para fins de aprendizado, como ocorre ao seguir este tutorial, você deseja excluir os recursos de nuvem que foram criados.
Embora esses recursos no contexto desse tutorial estejam em nível de uso gratuito, a melhor prática é limpar recursos não usados na nuvem.
Para limpar o projeto do Amplify, em um terminal, execute o seguinte comando:
amplify delete
Após algum tempo, você verá a mensagem abaixo confirmando que todos os recursos de nuvem foram excluídos.
✔ Project deleted in the cloud Project deleted locally.
Parabéns!
Você criou uma aplicação Web na AWS com êxito! Para continuar em grande estilo, aprofunde-se nas tecnologias específicas da AWS e leve sua aplicação para o próximo nível.