Alura > Cursos de Mobile > Cursos de iOS > Conteúdos de iOS > Primeiras aulas do curso SwiftUI: identificando erros em suas requisições

SwiftUI: identificando erros em suas requisições

Identificando os erros de uma requisição - Apresentação

Olá, gostaria de dar as boas-vindas a você que está iniciando mais um curso de IOS na Alura.

Audiodescrição: Ândriu Coelho é um homem branco de olhos castanhos e cabelo castanho-claro. Tem barba e bigode e usa uma camiseta azul-escura com o logotipo da Alura. Ao fundo, estúdio com iluminação gradiente do rosa para o azul. À direita, uma estante com decorações.

O que vamos aprender?

A ideia desse curso é continuar o desenvolvimento do aplicativo Vollmed, onde podemos procurar por especialistas e agendar uma consulta. Agora, vamos utilizar outro tópico, que é o tratamento de erros. O objetivo é pensar em casos de uso com caminhos alternativos.

Até então, estávamos focados no happy path (caminho feliz), mas sabemos que um aplicativo pode enfrentar instabilidade, ter problemas de conexão, e ocorrer vários tipos de erro no dia a dia. Nós, como pessoas desenvolvedoras, devemos saber tratar esses erros.

Vamos começar o curso configurando o Apiary, uma ferramenta em que podemos mockar (simular) uma resposta do back-end. Começaremos falando sobre status code, mais especificamente sobre erro 400. Vamos forçar um erro 400 utilizando o Apiary para verificar como o aplicativo se comporta.

Com base nisso, vamos avançar construindo um Snackbar de erro.

Snackbar de erro na parte inferior do aplicativo da Vollmed. Caixa de fundo vermelho e fonte branca com o texto: "Ops! Ocorreu um erro, mas já estamos trabalhando para solucionar".

Snackbar de erro é uma view que podemos utilizar para diferentes situações dentro do aplicativo, incluindo mostrar um erro. Podemos mostrar uma view com uma mensagem para a pessoa usuária entender o que está acontecendo. É muito importante manter a pessoa usuária sempre informada.

Feito isso, partiremos para outro tópico importante em nosso curso, que é o Skeleton. Quando abrimos o aplicativo e aparece uma view de carregamento, chamamos de Skeleton.

Skeleton da página inicial do aplicativo Vollmed. Uma silhueta cinza de um círculo e linhas estão no exato local onde ficarão a fotografia e demais informações de especialistas.

O aplicativo pode demorar para trazer informações, e é importante manter a pessoa usuária sabendo do que está acontecendo, mostrando que o aplicativo está tentando carregar as informações.

Pré-requisitos

Como pré-requisito, é importante que você tenha conhecimento em SwiftUI, ou que tenha feito os cursos da nossa formação sobre SwiftUI na Alura.

Esperamos que você tenha gostado dos tópicos que aprenderemos durante o curso. Até a primeira aula!

Identificando os erros de uma requisição - Gerenciando erros em aplicativos

Para iniciar nosso curso, vamos dar continuidade ao Vollmed, um aplicativo onde conseguimos procurar por especialistas, como médicos, e agendar consultas.

Este projeto foi desenvolvido no decorrer dos cursos anteriores dessa formação. Se você não acompanhou o passo a passo, pode voltar nos cursos anteriores para verificar como foi desenvolvido.

Status Code

Até então, trabalhamos primariamente com o happy path (caminho feliz). No entanto, sabemos que um aplicativo pode se tornar instável, porque não depende apenas dele. Fazemos requisições para servidores, dependemos de conexão com a internet, portanto, há vários problemas que podem acontecer.

A ideia neste curso é começar a trabalhar esses cenários. Daremos ênfase na importância de fornecer feedback para a pessoa usuária sobre o que está acontecendo no aplicativo.

Provavelmente, você já deve ter tido a experiência de usar algum aplicativo em que a tela travamos ou não entendeu o que estava acontecendo. Naquela ocasião, o problema poderia ter sido um erro não tratado ou internet lenta. Em todas essas situações, é importante manter a pessoa usuária informado sobre o que está acontecendo.

No curso em que falamos brevemente sobre o padrão arquitetural, começamos a mencionar sobre erros, mas não exploramos a fundo. No arquivo RequestError que temos em "Vollmed > Networking > Base", já mapeamos alguns casos de erro.

RequestError.swift:

enum RequestError: Error {
        case decode
        case invalidURL
        case noResponse
        case unauthorized
        case unknown
        case custom(error: [String: Any]?)

        var customMessage: String {
                switch self {
                case .decode:
                        return "erro de decodificação"
                case .unauthorized:
                        return "sessão expirada"
                default:
                        return "erro desconhecido"
                }
        }
}

Portanto, temos casos de erros como decodificação, URL inválida , sem resposta, não autorizado e erro desconhecido. Apesar de mapear alguns dos possíveis erros, não mostramos para a pessoa usuária o problema que está ocorrendo. Neste curso, vamos prosseguir a partir disso.

Para começar, falaremos sobre alguns status codes mais conhecidos.

No menu lateral esquerdo, abrimos o arquivo HTTPClient também dentro da pasta "Base". Nele, temos um switch/case que valida o de acordo com o statusCode.

HTTPClient.swift:

switch response.statusCode {

case 200...299:
        guard let responseModel = responseModel else {
                return .success(nil)
        }

        guard let decodedResponse = try? JSONDecoder().decode(responseModel, from: data) else {
                return .failure(.decode)
        }

        return .success(decodedResponse)
case 400:
        let errorResponse = try? JSONSerialization.jsonObject(with: data) as? [String: Any]
        return .failure(.custom(error: errorResponse))
case 401:
        return .failure(.unauthorized)

default:
        return .failure(.unknown)
}

Relembrando, o status code é um código, geralmente de três dígitos, que o back-end retorna para o client (no nosso caso, o aplicativo), informando o que aconteceu em uma requisição.

Os status codes da classe 200 a 299 geralmente indicam sucesso. Nesse arquivo já temos um caso de erro mapeado, que é o 401, quando não é autorizado, ou seja, quando tentamos fazer uma requisição e o token da pessoa usuária está inválido ou algo similar. Com base nesse status code, podemos tratar alguns erros.

Neste curso, vamos utilizar uma ferramenta chamada Apiary. Esta ferramenta é utilizada para documentar APIs e para simular (mock) a resposta de uma API.

É muito utilizada em equipes de desenvolvimento mobile, porque muitas vezes começamos a implementação e a equipe de back-end ainda não concluiu todo o desenvolvimento. Assim, conseguimos simular algumas respostas para consumir no front-end, no nosso caso, no nosso aplicativo.

Novo projeto no Apiary

Para começar, vamos abrir o navegador e procurar pela Apiary. Vamos acessar o primeiro link, que é a página oficial do Apiary. Para acessar a ferramenta, é necessário ter uma conta no GitHub.

Como já estamos logados na nossa conta, podemos clicar em "Continue" no canto superior direito para redirecionar a um projeto com um template padrão.

Vamos criar um novo projeto. Portanto, na barra superior esquerda, onde temos o nome da conta logada, vamos clicar no botão para expandir as opções. Em seguida, vamos clicar no botão verde "Create New API Project" para criar uma nova API. Com isso, podemos dar um nome para o projeto. Vamos nomeá-lo voll-med-api-erros e clicar em "Create API".

Depois de clicar, ele traz as configurações do Apiary à esquerda e algumas informações sobre o projeto à direita.

Modelo padrão das configurações do Apiary

# voll-med-api-erros

Polls is a simple API allowing consumers to view polls and vote in them.

## Questions Collection [/questions]

### List All Questions [GET]

+ Response 200 (application/json)
        [
                {
                        "question": "Favamosrite programming language?",
                        "published_at": "2015-08-05T08:40:51.6202",
                        "choices": [
                                {
                                        "choice": "Swift",
                                        "votes": 2048
                                },
                                {
                                        "choice": "Python",
                                        "votes": 1024
                                },
                                {
                                        "choice": "Objective-C",
                                        "votes": 512
                                },
                                {
                                        "choice": "Ruby",
                                        "votes": 256
                                }
                        ]
                }
        ]
        
### Create a New Question [POST]

(restante omitido…)

Podemos configurá-lo de várias maneiras, mas vamos começar simulando um erro no caso de agendar uma consulta com uma pessoa especialista. Ou seja, quando fazemos um GET para médicos especialistas. Por exemplo, vamos simular um erro clássico que pode acontecer, o erro 400.

Como fazemos para simular esse erro? No lado esquerdo do painel do Apiary, temos uma Response com o status code 200. Vamos alterar para 400. Portanto, vamos criar um mock que simula um erro 400 no nosso servidor.

Em List All Questions, configuramos o verbo da requisição, que continuará como GET.

E em Questions Collection é a rota da nossa API. Nesse caso, está a /questions, porque é um padrão que ele já traz. Mas vamos mudar para /specialists que significa especialista em inglês, conforme adotamos no projeto.

## Questions Collection [/specialists]

### List All Questions [GET]

+ Responde 400 (application/json)

Assim, quando se fizer um /specialists, ele retornará um erro 400. E, logo abaixo, podemos configurar um JSON de erro. Isso, geralmente, é acordado com a equipe de back-end, que define qual será o JSON de erro que eles vão enviar. Você pode inseri-lo nesse espaço para simular este comportamento.

Dica: ao usar a Apiary é importante manter dois "Tabs" de distância para configurar o código corretamente.

Vamos criar um JSON de erro. Após dois "Tabs", abrimos chaves para começar a criar um objeto de erro. Portanto, digitamos error entre aspas, seguido de dois-pontos e chaves.

Dentro destas chaves, vamos criar um objeto de erro. Primeiro, vamos enviar o código. Para isso, escrevemos code como 400, que é o status code de erro.

Também enviaremos uma mensagem, usando message. Poderíamos colocar qualquer mensagem. Nesse caso, vamos usar uma mensagem padrão para começar os nossos estudos. Entre aspas, escrevemos Ops! Ocorreu um erro, mas já estamos trabalhando para solucionar. Essa é a mensagem de erro inicial com a qual vamos trabalhar.

+ Responde 400 (application/json)

        {
            "error": {
                "code": 400,
                "message": "Ops! Ocorreu um erro, mas já estamos trabalhando para solucionar",
            }
        }

Em Create a New Question, temos o POST que não vamos utilizar. Portanto, podemos apagar desde esse título na linha 21 em diante.

Na linha 6, após o título principal voll-med-api-erros, temos uma breve descrição do que faz o endpoint /specialists. Vamos substituir a descrição padrão por Service used to simulate an error in the request. Ou seja, um serviço utilizado para simular um erro em uma requisição.

# voll-med-api-erros

Service used to simulate an error in the request

Portanto, a primeira etapa será a configuração do Apiary para simular um erro na resposta do servidor.

Na lateral direita, em "List All Questions", a ideia é substituir o endpoint que ele nos fornece e usar no nosso aplicativo. No dropdown de "Request", vamos trocar "Production" por "Mock Server" (servidor simulado). Com isso, o seguinte endpoint é fornecido:

https://private-854ce4-vollmedapierros.apiary-mock.com/specialists

Conclusão

Para iniciar, queíamos configurar o Apiary com você, explicar o que exploraremos na primeira etapa deste curso. No próximo vídeo, vamos começar a trabalhar no projeto para entender como configurar o projeto para tratar esses casos de erro.

Até o próximo vídeo.

Erros comuns - Trabalhando com endpoint de mock

Com o Apiary configurado, podemos utilizá-lo no nosso projeto. A ideia, na parte direita da tela, onde ele nos fornece o endpoint de mock que criamos no API, é que ele retorne, de fato, esse JSON que configuramos.

https://private-854ce4-vollmedapierros.apiary-mock.com/specialists

Copiamos toda essa URL, abrir uma nova aba no navegador e colar no campo de endereço na parte superior. Teclamos "Enter" para buscar o endereço.

The resource you're looking for doesn't exist.

Please check the API documentation or have a look on available resources below.

Ele está informando que o recurso ainda não existe, então é importante salvar todas as alterações que fizermos. Voltando ao editor de código, selecionamos o botão "Save", localizado do lado superior direito. Atualizamos a página no navegador novamente.

{
    "error": {
        "code": 400,
        "message": "Ops! Ocorreu um erro, mas já estamos trabalhando para solucionar"
    }
}

Note que ele já nos retorna o JSON que configuramos no API. É exatamente isso que o aplicativo vai ler na hora de acessar o endpoint de mock que criamos utilizando o API.

Agora podemos voltar ao projeto no Xcode.

Configurando a URL no projeto

Para conseguirmos configurar essa URL no nosso projeto, devemos abrir o arquivo HTTPClient.swift.

Do lado esquerdo do Xcode, temos a pasta Base, que está dentro de Networking, e abrimos o arquivo HTTPClient.swift .

Este arquivo contém a configuração dos endpoints que criamos no projeto. Para utilizar o que criamos na API, será necessário comentar todo esse bloco de código. Portanto, desde a linha 17 até a linha 21, vamos selecionar e apertar a tecla "Command + /" para comentar todas as linhas ao mesmo tempo.

HTTPClient.swift

// código omitido

extension HTTPClient {
    func sendRequest<T: Decodable>(endpoint: Endpoint, responseModel: T.Type?) async -> Result<T?, RequestError> {
        
//        var urlComponents = URLComponents()
//        urlComponents.scheme = endpoint.scheme
//        urlComponents.host = endpoint.host
//        urlComponents.path = endpoint.path
//        urlComponents.port = 3000

       guard let url = urlComponents.url else {
          return .failure(.invalidURL)
      }

// código omitido

Podemos criar uma URL fake de Apiary semelhante àquela que configuramos na plataforma do Apiary. Copiamos o nome dessa constante que criamos na linha 23 e na linha 25 vamos substituir esse urlComponents.url pela nova. Na verdade, como ela não é opcional, podemos até criar uma URL diferente. Não precisamos fazer essa verificação. Portanto, vamos voltar para como estava e comentamos da linha 25 à linha 27.

HTTPClient.swift

// código omitido
        
//        var urlComponents = URLComponents()
//        urlComponents.scheme = endpoint.scheme
//        urlComponents.host = endpoint.host
//        urlComponents.path = endpoint.path
//        urlComponents.port = 3000

        let urlApiary = URL "https://private-854ce4-vollmedapierros.apiary-mock.com/specialists"

//        guard let url = urlComponents.url else {
//            return .failure(.invalidURL)
//        }

// código omitido

Vamos criar uma nova URL. Na linha 29, let url é igual a URL(). Vamos instanciar usando um inicializador com string. E passamos a URL. Copiamos a linha 23 e colamos na linha 29, onde podemos apagar a linha 23 (let urlApiary = URL "https://private-854ce4-vollmedapierros.apiary-mock.com/specialists").

HTTPClient.swift

// código omitido
        
//        var urlComponents = URLComponents()
//        urlComponents.scheme = endpoint.scheme
//        urlComponents.host = endpoint.host
//        urlComponents.path = endpoint.path
//        urlComponents.port = 3000

//        guard let url = urlComponents.url else {
//            return .failure(.invalidURL)
//        }

let url = URL(string: 
"https://private-854ce4-vollmedapierros.apiary-mock.com/specialists") 

// código omitido

Agora, podemos retornar uma URL opcional. Como, por exemplo, se digitarmos "fsdafdsfsdfsa" não é uma URL. Então, não conseguiremos criar esse objeto. Por ser opcional, podemos utilizar um guard. E caso não consigamos, retornaremos uma falha usando o else{}.

HTTPClient.swift

// código omitido
        
guard let url = URL(string: "https://private-854ce4-vollmedapierros.apiary-mock.com/specialists") else {
        return .failure(.invalidURL)
}

// código omitido

Já temos uma URL para acessar o mock que criamos na API. Rodamos o aplicativo para testar, na parte superior esquerda, clicamos em "Run". E veremos o que acontece.

Ele está subindo o simulador do projeto e ao finalizar o carregamento, obtemos:

Skeleton da página inicial do aplicativo Vollmed. Na parte superior central há o ícone da Vollmed, abaixo o texto "Boas-vindas!" e na sequência o texto "Veja abaixo os especialistas da Vollmed disponíveis e marque já a sua consulta!".

Isso é importante para também pensarmos nos caminhos alternativos do nosso projeto. Quando ele consegue recuperar os especialistas disponíveis é exibido uma lista e o aplicativo fica conforme o esperado

Porém, quando ele não consegue obter essas informações, fica uma tela branca abaixo da mensagem "Veja abaixo os especialistas de "Vollmed" disponíveis e marque sua consulta". Mas a pessoa usuária tentará visualizar e não terá sucesso, então devemos também pensar nos caminhos alternativos.

Nesse caso, estamos simulando um erro 400. Para tratarmos desse erro, vamos abrir novamente o arquivo onde estamos mapeando o erro. Temos o enum de erro, no arquivo RequestError .

RequestError

// código omitido

enum RequestError: Error {
    case decode
    case invalidURL
    case noResponse
    case unauthorized
    case unknown

// código omitido

E no arquivo HTTPClient.swift , temos o switch case, onde estamos tratando o erro de acordo com o statusCode, na linha 46.

Criando um case para o erro 400

A ideia é criar um novo case para a classe de erro 400. Então, na linha 59, temos esse retorno do sucesso. Vamos inserir um novo código e digitamos o caso de erro 400. Se der erro 400, vamos fazer alguma coisa com esse erro.

Primeiro, precisamos tentar recuperar a mensagem de erro que o servidor devolve. Para tal, criamos uma constante chamada errorResponse.

Voltando ao navegador onde obtivemos o JSON, precisamos conseguir acessar a mensagem que o servidor nos devolve de alguma forma, e conseguimos fazer isso fazendo a decodificação da resposta.

Ops! Ocorreu um erro, mas já estamos trabalhando para solucionar

Voltamos ao Xcode.

Para acessarmos essa mensagem, usamos o JSONSerialization, que é uma try function, então precisamos usar try?.

Na linha 59, chamamos JSONSerialization.jsonObject() passando o data, que são os dados que o servidor nos devolve. Converteremos isso para um dicionário de [string:Any], ou seja, a chave do dicionário é uma string e o valor não tem um tipo definido.

Para visualizarmos no console, vamos usar um print() de errorResponse. Por enquanto, vamos retornar um caso de falha, mas vamos alterar isso depois. Vamos usar invalidURL para conseguir buildar o projeto.

HTTPClient.swift

// código omitido

case 400:
let errorResponse = try? JSONSerialization.jsonObject(with: data) as? [String: Any]
print(errorResponse)

return.failure(.invalidURL)

// código omitido

Vamos colocar um breakpoint, ou seja, um aviso que colocamos no código para a execução parar em determinado ponto, para que possamos inspecionar o valor da nossa variável. Na linha 59, clicamos acima do número "59" do lado esquerdo. Observem que agora temos uma flecha na cor roxa acima do número, indicando que o código será interrompido nesse ponto.

Compilamos o projeto novamente clicando no botão de play na parte superior esquerda para visualizarmos se conseguiremos ler a mensagem de erro ou não.

Quando a linha fica verde, significa que o código está parado nesse ponto onde configuramos. Na parte inferior direita, digitamos uma linha de comando para conseguirmos ler o valor desse erro, mas ele precisa passar por essa linha primeiro.

No canto inferior esquerdo, selecionamos o segundo ícone de step over (uma base com uma seta acima). Do lado direito, digitamos po print(errorResponse).

Por algum motivo, ele não está deixando imprimir o valor, mas como colocamos um print na linha 60, faremos um step over e podemos visualizar se ele exibe o valor ali.

Obtemos:

Optional (["error": { 
code = 400;
message = "Ops! Ocorreu um erro, mas já estamos trabalhando para solucionar"; }])

Do lado direito é exibido o error, o status code que é o 400, e a mensagem, que é exatamente o que configuramos aqui no API. Então, estamos conseguindo capturar essas informações no nosso aplicativo, exatamente isso que estamos lendo.

Como fazemos para continuar a execução do programa? Seguramos a linha 59 e arrastamos para fora e clicamos em continuar a execução do programa no primeiro ícone no canto inferior esquerdo.

Conclusão e Próximos Passos

Então o objetivo desse vídeo foi usarmos o mock que criamos no API para conseguirmos capturar a mensagem de erro. Agora nós temos um material, que é essa mensagem de erro, para informar ao usuário o que está acontecendo em nosso aplicativo.

Vamos continuar com isso a seguir!

Sobre o curso SwiftUI: identificando erros em suas requisições

O curso SwiftUI: identificando erros em suas requisições possui 96 minutos de vídeos, em um total de 36 atividades. Gostou? Conheça nossos outros cursos de iOS em Mobile, ou leia nossos artigos de Mobile.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda iOS acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas