Conceitos básicos da AWS

Criar uma aplicação iOS

Criar uma aplicação iOS simples usando o AWS Amplify

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.

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

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.

Agradecemos por seguir este tutorial até o fim. Deixe seus comentários usando a ferramenta a seguir ou uma solicitação pull no repositório Github.

Este módulo foi útil?

Agradecemos a sua atenção
Gostaríamos de saber do que você gostou.
Fechar
Desculpe por ter desapontado
Encontrou algo desatualizado, confuso ou incorreto? Envie seus comentários para nos ajudar a melhorar este tutorial.
Fechar

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.