Olá! Me chamo Giovanna Moeller e sou instrutora aqui na Alura.
Audiodescrição: Giovanna Moeller é uma mulher branca, de cabelos loiros, lisos e longos. Veste uma camiseta azul com o texto "Alura".
Boas-vindas a mais um curso de SwiftUI, este envolvendo operações CRUD e requisições para uma API externa.
No projeto que vamos construir, denominado Voll Med, representaremos uma clínica médica na qual as pessoas podem ver uma lista de especialistas. As pessoas podem agendar uma consulta por meio de um calendário, selecionando a data e o horário.
Será possível verificar suas consultas agendadas, remarcar uma consulta selecionando outra data e horário, ou cancelar uma consulta. Ao cancelar, será necessário fornecer o motivo do cancelamento.
Durante este curso, você aprenderá a lidar com as quatro operações principais em um aplicativo: criação de dados, leitura de dados, atualização de dados e remoção de dados.
Principais Operações
É essencial dominar essas operações, pois, no cotidiano da pessoa desenvolvedora mobile, estamos constantemente comunicando com serviços externos e lidando com dados.
Os pré-requisitos desse curso incluem conhecimento prévio em construção de layouts com SwiftUI e seus principais componentes, além de noções básicas de requisições HTTP. Mas não se preocupe, também faremos uma revisão desses conceitos.
Se tiver qualquer dúvida, não hesite em postá-la no fórum ou nos contatar no Discord. Estaremos sempre disponíveis para ajudar.
Agora, vamos começar este projeto.
Te espero na primeira aula!
Vamos iniciar compreendendo o projeto inicial.
Este curso não se concentra em layout, por isso já trouxemos um projeto inicial para enfocar no objetivo deste curso, que é realizar requisições com uma API externa.
Com o XCode aberto, estou utilizando a versão 15
, que neste momento está em beta, o que significa que não está disponível para todo o público. No entanto, acreditamos que quando vocês estiverem assistindo a este curso, essa versão estará em release, ou seja, disponível para todos.
Começaremos executando nosso projeto teclando "Command + R", e ele abrirá o simulador. Estamos utilizando o simulador do iPhone 14, que está na versão do iOS 17, mas não importa qual simulador estaremos utilizando.
Nossa tela inicial do aplicativo é composta por uma imagem, um texto de boas-vindas e um texto para apresentar os especialistas da Voll Med disponíveis. Além disso, há uma lista de especialistas que é completamente estática. Mostraremos por que essa lista é estática.
Iniciaremos analisando o código pela pasta Assets
, em que temos a seguinte estrutura:
Nela, temos a AccentColor
, que é a cor de destaque da nossa aplicação, composta por um azul mais escuro.
Há o ícone da nossa aplicação, o "API", com o ícone do aplicativo e uma pasta Colors
, que contém duas cores. A primeira cor é uma vermelha, chamada de Cancel
, e a segunda cor é um azul mais claro, chamado de Light Blue
.
Por fim, temos duas imagens: uma imagem estática de uma pessoa doutora, que será alterada, pois vamos realizar o download de imagens de acordo com a URL que vem da API, mas isso é assunto para os próximos capítulos. Além disso, temos uma imagem da logo da nossa aplicação.
A nossa tela inicial é a "Home View". Vamos abrir o arquivo "VollMedApp":
VollMedApp
// VollmedApp.swift
// Vollmed
//
// Created by Giovanna Moeller on 12/09/23.
//
import SwiftUI
@main
struct VollmedApp: App {
var body: some Scene {
WindowGroup {
HomeView()
}
}
}
Neste arquivo, observamos que a "Home View" está sendo chamada ao invés da "ContentView":
ContentView
//
// ContentView.swift
// Vollmed
// Created by Giovanna Moeller on 12/09/23.
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Image (systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
}
Text("Hello, world!")
.padding()
}
}
# Preview {
ContentView()
}
A "ContentView" é a tela padrão que vem com todo o projeto inicializado em SwiftUI. Nesse momento, ela está exatamente como veio originalmente, e não vamos mexer nela por enquanto.
Selecionamos o arquivo HomeView
do lado esquerdo do Xcode:
HomeView
import SwiftUI
struct HomeView: View {
var body: some View {
ScrollView(showsIndicators: false) {
VStack {
Image(.logo)
.resizable()
.scaledToFit()
.frame(width: 200)
.padding(.vertical, 32)
Text("Boas-vindas!")
.font(.title2)
.bold()
.foregroundColor(Color(.lightBlue))
Text("Veja abaixo os especialistas da Vollmed disponíveis e marque já a sua consulta!")
.font(.title3)
.bold()
.foregroundColor(.accentColor)
.multilineTextAlignment(.center)
.padding(.vertical, 16)
ForEach(specialists) { specialist in
SpecialistCardView(specialist: specialist)
.padding(.bottom, 8)
}
}
.padding(.horizontal)
}
.padding(.top)
}
Observem que temos uma ScrollView()
, pois podemos ter vários especialistas e o usuário precisa rolar a tela. Temos uma imagem, e eu estou importando esta imagem do Assets
de uma maneira um pouco diferente, graças a uma nova funcionalidade do Xcode 15.
Antes, precisávamos referenciar com uma String (Image("Logo")
), mas agora basta escrever um ponto e o nome da imagem que está no Assets
. A mesma coisa ocorre com a cor. Na linha 22, onde temos esse foregroundColor
, colocamos Color(.lightblue)
, que é a nossa cor lightblue
lá do Assets
.
Essa é uma nova funcionalidade do Xcode 15 que gostei bastante, porque evita muitos erros.
Portanto, temos neste arquivo uma imagem com alguns modificadores de propriedade e também um texto com outros modificadores. Finalmente, temos esse forEach
, no qual a nossa variável é especialistas.
O componente SpecialistCardView
é definido dentro da nossa pasta components
, que está localizada dentro da pasta views
. Do lado esquerdo, clicamos no arquivo SpecialistCardView
:
SpecialistCardView
import SwiftUI
struct SpecialistCardView: View {
var specialist: Specialist
var body: some View {
VStack(alignment: .leading) {
HStack(spacing: 16.0) {
Image(.doctor)
.resizable()
.scaledToFill()
.frame(width: 64, height: 64)
.clipShape (Circle())
VStack(alignment: .leading, spacing: 8.0) {
Text(specialist.name)
.font(.title3)
.bold()
}
}
Text (specialist.specialty)
}
}
ButtonView(text: "Agendar Consulta")
}
// código omitido
Observem que há a variável especialist
que estamos passando, temos um VStack
, no qual os elementos estão alinhados à esquerda.
Também temos um HStack
, que é o nosso container horizontal que possui um espaçamento de 16, e, dentro desse container, encontramos uma imagem estática do Assets
e um outro container vertical, onde apresentamos o nome da pessoa especialista e a sua especialidade. Também temos um botão, que é um componente personalizado criado por mim.
Do lado esquerdo, dentro de components
, clicamos no arquivo ButtonView
.
ButtonView
// código omitido
import SwiftUI
enum ButtonType {
case primary
case cancel
}
struct ButtonView: View {
var text: String
var buttonType: ButtonType
init(text: String, buttonType: ButtonType = .primary) {
self.text = text
self.buttonType = buttonType
}
var body: some View {
Text(text)
.bold()
.foregroundColor(.white)
.frame(maxWidth: .infinity)
.padding(.vertical, 14)
.background(buttonType == .primary ? Color.accentColor :
Color(.cancel))
.cornerRadius(12)
.padding(.top, 8)
}
}
#Preview {
ButtonView(text: "Agendar consulta")
}
Temos como parãmetro um texto do tipo string e na sequência o tipo do botão. Este tipo de botão é definido pelo enum ButtonType
que possui dois casos: o caso primário (primary
) e de cancelamento (cancel
).
Da linha 19 (initi()
) até a 22, temos um construtor em que passamos os parâmetros de texto e tipo de botão. No entanto, caso não passarmos nenhum valor para o ButtonType
será assumido que o valor padrão será o primary
.
Qual a diferença entre primary
e cancel
? Somente a cor. Na linha 30 (background()
) fazemos uma verificação ternária na cor de fundo. Quando o tipo deo botão for primário a cor será accentColor
, sendo o azul escuro. Já se for cancel
, inserimos a cor vermelha.
No Preview
, como não estamos passando nenhum valor para o parâmetro ButtonType
, por padrão, assume que é da cor primária. No entanto, se passarmos buttonType: .cancel
a cor será alterada para vermelha no emulador.
#Preview {
ButtonView(text: "Agendar consulta", buttonType: .cancel)
}
Esse efeito será útil quando tivermos o cancelamento de uma consulta.
Do lado esquerdo, encontramos uma pasta intitulada Services
, que inclui um único arquivo denominado WebService
, sendo uma struct
com o mesmo nome.
WebService
import Foundation
struct Web Service {
private let baseURL = "http://localhost:3000"
func getAllSpecialists() {
}
}
Nessa struct
, iremos consolidar todas as nossas chamadas para a API. Além disso, definimos uma URL base, que corresponde à URL da nossa API. A API será executada localmente, na nossa própria máquina, por isso essa é a nossa URL base.
Temos também uma função chamada getAllSpecialists
que está vazia.
Na HomeView
, temos um forEach
da variável specialist
. Essa variável está definida em uma pasta chamada models
, e dentro dela temos um arquivo chamado Specialist
, sendo uma struct
que faz referência ao nosso modelo de especialista.
Specialist
import Foundation
struct Specialist: Identifiable, Codable {
let id: String
let name: String
let rm: String
let imageUrl: String let specialty: String
let email: String
let phoneNumber: String
enum CodingKeys: String, CodingKey {
case id
case name = "nome"
case crm
case imageUrl = "imagem"
case specialty = "especialidade"
case email
case phoneNumber = "telefone"
}
}
// código omitido
Quando pegamos a lista de especialistas da API, os especialistas têm as seguintes propriedades: ID, nome, CRM, uma URL de imagem para download posterior, especialidade, e-mail e número de telefone.
No entanto, quando pegamos essas informações da API, as chaves que vêm no nosso arquivo JSON estão em português. Para resolver isso, definimos o enum CodingKeys
na linha 19, para fazer o mapeamento entre a chave JSON, que será retornada da API, para este modelo de dados, essa struct
que estamos criando. Isso porque é considerado uma boa prática escrever o código em inglês.
Por esse motivo, estamos definindo os nossos atributos em inglês. No entanto, na API, eles estão em português, e precisamos converter essas chaves para este modelo de dados para evitar erros na decodificação.
Mais adiante, explicarei o funcionamento da nossa API, como coletamos essas informações e como elas são retornadas. Por enquanto, isso é o que precisamos entender.
Specialist
// código omitido
let specialists: [Specialist] = [
Specialist(id: "c84k5kf", name: "Dr. Carlos Alberto", crm: "123456", imageUrl:
"https://images.unsplash.com/photo-1637059824899-a441006a6875?ixlib=rb-4.0 .3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWd1fHx8fGVufDB8fHx8fA%3D 3D&auto=format&fit=crop&w=752&q=
80", specialty: "Neurologia", email: "carlos.alberto@example.com", phoneNumber: "(11) 99999-9999"),
Specialist(id: "1349493", name: "Dra. Maria Aparecida", crm: "789101", imageUrl:
"https://images.unsplash.com/photo-1651008376811-b98baee60c1f?ixlib=rb-4.0 .3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWd1fHx8fGVufDB8fHx8fA%3D %30&auto=format&fit=crop&w=687&q= 80", specialty: "Cardiologia", email: "maria.aparecida@example.com", phoneNumber: "(21)
88888-8888"),
Specialist(id: "4435h553", name: "Dr. João Ribeiro", crm: "123987", imageUrl:
"https://images.unsplash.com/photo-1622253694242-abeb37a33e97?ixlib=rb-4.0 .3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWd1fHx8fGVufDB8fHx8fA % 3D %30&auto=format&fit=crop&w=765&q= 80", specialty: "Oftalmologia", email: "joao.ribeiro@example.com", phoneNumber: "(11) 77777-7777"),
]
Por fim, temos um array temporário chamado Specialists
. Este é um array do tipo Specialist, no qual instanciamos três vezes um especialista com diferentes informações. Novamente, isso é temporário, pois vamos utilizar dados da API, tornando tudo muito mais dinâmico.
Este é o nosso projeto inicial. Analise o código do projeto para se familiarizar, pois é a partir dele que desenvolveremos todo o projeto.
Espero que este vídeo tenha auxiliado na compreensão da estrutura do nosso projeto inicial.
Aguardo sua presença no próximo vídeo, onde exploraremos com mais detalhes o funcionamento desta API.
Agora, vamos compreender o funcionamento da API externa, suas rotas disponíveis, os dados a serem enviados em cada rota e as ações que cada rota realiza. Para essa finalidade, utilizaremos a ferramenta chamada Insomnia.
O Insomnia é uma aplicação visual destinada a testar APIs, e ele também pode ser empregado como uma forma de documentação. Visto que não possuímos uma documentação escrita para nossa API, utilizaremos o Insomnia para obter uma compreensão mais detalhada sobre seu funcionamento.
O Insomnia permite que as pessoas desenvolvedoras interajam com APIs sem a necessidade de escrever códigos para as solicitações HTTP. Podemos fazer diversas requisições, utilizando os verbos como GET
, POST
, PUT
, PATCH
e DELETE
.
Também podemos especificar o que precisamos enviar para cada requisição. Por exemplo, os cabeçalhos, denominados como headers, que são informações úteis sobre as requisições. Além disso, podemos observar o retorno de cada requisição.
Nesta aula, disponibilizaremos um arquivo para download no formato JSON. Esse arquivo será importado para o Insomnia.
Para realizar esse processo, na tela inicial do Insomnia, clicamos no botão "Import" localizado no centro da tela. Em seguida, selecionamos o arquivo VollMed-CRUD.json
e clicamos em "Abrir". Depois, escolhemos o botão "Scan" no canto inferior direito e, em seguida, clicamos em "Import".
Será criada uma coleção (collection) chamada VollMed-CRUD.json
.
Clicamos na coleção, e ao lado esquerdo, podemos observar diversas rotas e pastas:
POST
loginPOST
logoutGET
Todos os pacientesPOST
Cadastro pacienteGET
Consultas de um p...GET
Retorna todos os e...GET
Retorna especialist...POST
Cria especialista(s)Por exemplo, temos uma rota GET
para buscar todos os pacientes. Se clicarmos nesta opção e depois em "Send" na parte superior direita, sem ter nenhum paciente cadastrado, ocorrerá um erro.
"Não encontramos pacientes!"
Nota importante: a API precisa estar em execução. Teremos um material explicando como executar a API.
Também temos a requisição POST
para criar um paciente. Essa requisição recebe um JSON com os atributos: CPF, nome, e-mail, senha, telefone e um plano de saúde.
{
"cpf": "65147182055",
"nome": "Lucas",
"email": "lucas@email.com",
"senha": "12345",
"telefone": 99999999,
"planoSaude": "Unimed"
}
Ao clicar em "Send", será criado um paciente e recebemos como retorno um objeto do tipo paciente, com um atributo adicional, um ID. O código da requisição (status code
) será 202
, significando sucesso.
{
"cpf": "65147182055",
"nome": "Lucas",
"email": "lucas@email.com",
"senha": "115523486ad8986ddbd42a6a6d3f830d:bf9da3247c44e6521755540774d17db3",
"telefone": 99999999,
"planoSaude": "Unimed",
"id": "e34db020-3445-4fbf-a744-ba578754da04"
}
Podemos também criar especialistas com a rota POST
"Cria Especialistas" selecionando "Send". Os especialistas criados serão utilizados posteriormente em nosso aplicativo.
Na parte inferior da rota ou URL, temos algumas variáveis. Uma delas é a headers (cabeçalhos) da requisição, que possui uma propriedade chamada Content-Type
, sendo Application/JSON
, significando que estamos enviando um arquivo do tipo JSON para a API.
POST
Cria uma consultaGET
Lista todas consultasGET
Lista consulta por IDTambém temos as rotas de consulta na seção "Consulta". A rota POST
para criar uma consulta, a rota GET
para listar todas as consultas e a rota GET
por ID para listar consultas. A rota PATCH
para atualizar uma consulta e a rota DELETE
para apagar uma consulta.
Essa é a funcionalidade básica da nossa API. Teremos um módulo sobre autenticação, mas por agora, no Insomnia, temos todas as rotas disponíveis, o que cada rota recebe, o que precisamos enviar para cada rota e qual é o retorno quando fazemos as requisições.
Agora que já entendemos o funcionamento, é hora de explorar o Insomnia, testar as requisições e analisar o que acontece. Depois disso, podemos começar a consumir esta API no nosso aplicativo iOS.
Até o próximo vídeo!
O curso iOS com SwiftUI: construindo aplicações interativas com CRUD e APIs REST possui 166 minutos de vídeos, em um total de 61 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:
Impulsione a sua carreira com os melhores cursos e faça parte da maior comunidade tech.
1 ano de Alura
Assine o PLUS e garanta:
Formações com mais de 1500 cursos atualizados e novos lançamentos semanais, em Programação, Inteligência Artificial, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
A cada curso ou formação concluído, um novo certificado para turbinar seu currículo e LinkedIn.
No Discord, você tem acesso a eventos exclusivos, grupos de estudos e mentorias com especialistas de diferentes áreas.
Faça parte da maior comunidade Dev do país e crie conexões com mais de 120 mil pessoas no Discord.
Acesso ilimitado ao catálogo de Imersões da Alura para praticar conhecimentos em diferentes áreas.
Explore um universo de possibilidades na palma da sua mão. Baixe as aulas para assistir offline, onde e quando quiser.
Acelere o seu aprendizado com a IA da Alura e prepare-se para o mercado internacional.
1 ano de Alura
Todos os benefícios do PLUS e mais vantagens exclusivas:
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos, corrige exercícios e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com a Luri até 100 mensagens por semana.
Aprenda um novo idioma e expanda seus horizontes profissionais. Cursos de Inglês, Espanhol e Inglês para Devs, 100% focado em tecnologia.
Transforme a sua jornada com benefícios exclusivos e evolua ainda mais na sua carreira.
1 ano de Alura
Todos os benefícios do PRO e mais vantagens exclusivas:
Mensagens ilimitadas para estudar com a Luri, a IA da Alura, disponível 24hs para tirar suas dúvidas, dar exemplos práticos, corrigir exercícios e impulsionar seus estudos.
Envie imagens para a Luri e ela te ajuda a solucionar problemas, identificar erros, esclarecer gráficos, analisar design e muito mais.
Escolha os ebooks da Casa do Código, a editora da Alura, que apoiarão a sua jornada de aprendizado para sempre.