Um clone do Instagram usando SwiftUI e GraphQL – Login
Em nossa postagem anterior sobre como criar um aplicativo clone do Instagram, você aprendeu a configurar tudo para ter o SwiftUI em funcionamento no XCode 11 e criou uma visualização de Sing Up totalmente funcional com GraphQL.
Hoje, aprenderemos a criar uma visualização de login e a fazer com que o usuário faça logout.
Precisaremos do projeto da postagem anterior, portanto, se você não acompanhou a postagem anterior, sugiro enfaticamente que o faça.
Apertem os cintos de segurança e vamos lá!
Para um melhor aprendizado, faça o download do projeto iOS Instagram Clone com o código-fonte.
Contents
Deseja um início rápido?
Clone esse aplicativo em nosso Hub e comece a usá-lo sem problemas!
Criação da visualização de login
Nossa visualização de login será bastante semelhante à visualização de inscrição, na verdade, ainda mais simples.
Em nosso logInUser Mutation, precisamos apenas de dois parâmetros: nome de usuário e senha:
As consultas e mutações dependerão da versão do Parse que você escolheu:
Parse 3.7.2:
mutação logInUser($username: String!, $password: String!){
usuários{
logIn(nome de usuário: $username, senha: $password){
sessionToken
}
}
}
Parse 3.8:
mutação logInUser($username: String!, $password: String!){
logIn(username: $username, password: $password){
sessionToken
}
}
Parse 3.9:
mutação logInUser($username: String!, $password: String!){
logIn(username: $username, password: $password){
sessionToken
}
}
portanto, só precisaremos perguntar isso para os nossos usuários.
Vamos começar adicionando uma nova visualização SwiftUI, indo para File > New > File e selecionando nossa visualização SwiftUI
Vamos nomear essa visualização como LogInView.swift e adicioná-la ao nosso projeto:
E, como você já aprendeu, crie sua VStack com os controles de que precisaremos:
- TextField para o nome de usuário
- SecureField para a senha
- Botão para executar a ação
Para manter a consistência do design, movi as cores que usaremos para o AppDelegate.swift, portanto, tive que importar o SwiftUI para lá também:
import SwiftUI let lightGreyColor = Color(red: 239.0/255.0, green: 243.0/255.0, blue: 244.0/255.0, opacity: 1.0) let lightBlueColor = Color(red: 36.0/255.0, green: 158.0/255.0, blue: 235.0/255.0, opacity: 1.0)
Lembre-se de remover as linhas de cor do SignUpView.swift e do LogInView.swift.
Além disso, para manter a consistência dos controles, apenas copiei e colei da nossa visualização SignUp e removi o TextField de e-mail e alterei os TextFields para refletir as novas funcionalidades. Meu código ficou assim:
struct LogInView: View {
@State var nome de usuário: String = ""
@State var password: String = ""
@State private var showingAlert = false
var body: some View {
VStack{
Text("Log In")
.font(.largeTitle)
.foregroundColor(lightBlueColor)
.fontWeight(.semibold)
.padding(.bottom, 20)
TextField("Username", text: $username)
.padding()
.background(lightGreyColor)
.cornerRadius(5.0)
.padding(.bottom, 20)
SecureField("Password", text: $password)
.padding()
.background(lightGreyColor)
.cornerRadius(5.0)
.padding(.bottom, 20)
Button(action: {
}){
Texto("Log In!")
.font(.headline)
.foregroundColor(.white)
.padding()
.frame(largura: 220, altura: 60)
.background(lightBlueColor)
.cornerRadius(15.0)
}
}.padding()
}
}
Isso foi simples. Vamos ver como fica?
Altere seu ContentView.swift para mostrar essa visualização:
struct ContentView : View {
var body: some View {
LogInView()
}
}
Ficou bem legal!
Vamos deixá-lo mais organizado adicionando nosso logotipo na parte superior!
Nosso logotipo consistirá em uma imagem que será integrada ao nosso projeto. Eu usei esta aqui.
Arraste e solte essa imagem em sua pasta Assets.xcassets no projeto:
Ela deverá ter a seguinte aparência:
De agora em diante, podemos fazer referência a ela em nosso código com esse nome: logo-social.
Inscreva-se agora no Back4App e comece a criar seu aplicativo clone do Instagram.
Nosso logotipo
Nosso logotipo consistirá nessa imagem, mas simplesmente colocar a imagem ali pareceria amador. Nós o faremos brilhar: circular, com um tamanho fixo, alguns traços nas bordas e, é claro, sombra projetada por causa da… sombra projetada.
O código para tudo isso é o seguinte:
Imagem("logo-social")
.redimensionável()
.aspectRatio(contentMode: .fit)
.frame(largura: 150, altura: 150)
.clipShape(Circle())
.overlay(Circle().stroke(Color.blue, lineWidth: 2))
.shadow(radius: 5)
.padding(.bottom, 75)
E ele vai para o topo da nossa VStack:
var body: some View {
VStack{
Imagem("logo-social")
.redimensionável()
.aspectRatio(contentMode: .fit)
.frame(width: 150, height: 150)
.clipShape(Circle())
.overlay(Circle().stroke(Color.blue, lineWidth: 2))
.shadow(radius: 5)
.padding(.bottom, 75)
Texto ("Log In")
.font(.largeTitle)
.foregroundColor(lightBlueColor)
.fontWeight(.semibold)
.padding(.bottom, 20)
...
Não é lindo?
O sessionToken
Nosso processo de login retornará uma string sessionToken. Devemos manter esse sessionToken seguro, pois ele será usado durante nossas operações: enquanto esse sessionToken for válido, teremos acesso ao nosso aplicativo. Quando ele for excluído ou invalidado, nossas chamadas serão negadas.
Como isso está diretamente relacionado à segurança, precisaremos armazená-lo de forma segura. O local correto para fazer isso é o Keychain no iOS.
O único problema do Keychain é que ele é difícil e chato de usar, por isso decidi usar esse wrapper para gerenciá-lo. Ele torna a vida muito mais fácil e, como o Keychain é um aplicativo de segurança, ele é muito fácil de usar. Isso facilita muito a vida e, como já estamos usando Cocoapods, faz todo o sentido.
Vamos editar nosso Podfile e adicionar isso aos nossos pods:
pod 'SwiftKeychainWrapper'
Em seguida, vamos atualizar nossos pods com o comando
pod install
e, por fim, reabrir nosso projeto xcworkspace.
Agora estamos prontos para usar nosso mecanismo Keychain para…
Armazenar o sessionToken
De acordo com a documentação do nosso novo pod, a maneira de salvar um valor no keychain é
KeychainWrapper.standard.set("SomeValue", forKey: "SomeKey")
mas vamos adicionar alguma lógica a ele também.
Para começar, vamos importar nosso wrapper:
importar SwiftKeychainWrapper
A primeira coisa é que teremos de chamar nossa mutação logInUser e, quando ela responder, armazenaremos o sessionToken, se houver um. Caso contrário, teremos de notificar o usuário com um alerta.
Agora, se você se lembra do artigo anterior, já temos um alerta codificado, incluindo sua estrutura. Vamos reutilizá-lo removendo o código abaixo do nosso SingUpView.swift e passando-o para o nosso arquivo AppDelegate.swift para que todas as exibições possam acessá-lo:
struct Message {
var alertTitle: String = ""
var alertText: String = ""
}
var myMessage = Message()
Agora, voltando à nossa lógica, a primeira coisa que temos de fazer é determinar se o usuário preencheu as caixas de texto de nome de usuário e senha. Caso contrário, não há informações para o processo de login e devemos notificar o usuário sobre isso.
Nosso código de ação para o botão de login deve verificar isso. Vamos adicionar esse trecho de código que verifica o tamanho da string das variáveis de estado vinculadas a esses campos de texto:
Button(action: {
// Verificar se há uma senha digitada
if (self.password.count == 0 || self.username.count == 0){
// Caso contrário, devemos mostrar o alerta
myMessage.alertText = "Você deve fornecer um nome de usuário e uma senha".
myMessage.alertTitle = "Oops..."
self.showingAlert = true
} else {
// Se sim, podemos prosseguir
}
}){
Text("Log In!")
.font(.headline)
.foregroundColor(.white)
.padding()
.frame(largura: 220, altura: 60)
.background(lightBlueColor)
.cornerRadius(15.0)
}
.alert(isPresented: $showingAlert) {
Alert(title: Text(myMessage.alertTitle), message: Text(myMessage.alertText), dismissButton: .default(Text("OK"))))
}
e testá-lo…
Legal!
Agora precisamos chamar nossa mutação GraphQL para recuperar o sessionToken e, se obtivermos algum, armazená-lo no Keychain.
Você já aprendeu como chamar as mutações, então vamos fazer isso, mas, desta vez, se obtivermos o sessionToken, vamos armazená-lo:
// Executar a mutação LogInUser, passando os parâmetros que acabamos de obter de nossos TextFields
apollo.perform(mutation: LogInUserMutation(username: self.username, password: self.password)){ result in
// Vamos alternar o resultado para que possamos separar um resultado bem-sucedido de um erro
switch result {
// Em caso de sucesso
case .success(let graphQLResult):
// Tentamos Parse nosso resultado
if let sessionToken = graphQLResult.data?.users?.logIn.sessionToken {
myMessage.alertTitle = "Yay!"
myMessage.alertText = "O usuário se conectou!"
self.showingAlert = true
print ("User sessionToken " + sessionToken)
// Gravar o sessionToken em nosso Keychain
let _: Bool = KeychainWrapper.standard.set(sessionToken, forKey: "Back4Gram.sessionToken")
}
// mas em caso de erros do GraphQL, apresentamos essa mensagem
else if let errors = graphQLResult.errors {
// Erros do GraphQL
myMessage.alertTitle = "Oops!"
myMessage.alertText = "Temos um erro de GraphQL: " + errors.description
self.showingAlert = true
print(errors)
}
// Em caso de falha, apresentamos essa mensagem
case .failure(let error):
// Erros de rede ou de formato de resposta
myMessage.alertTitle = "Oops!"
myMessage.alertText = "Ocorreu um erro: " + error.localizedDescription
self.showingAlert = true
print(error)
}
}
Vamos testá-lo!
Que legal! Mas como sabemos se realmente funcionou?
Podemos acessar o Parse Dashboard do nosso aplicativo e verificar se há um novo objeto Session gravado:
Como um encanto!
E já que estamos aqui…
Que tal adicionar um botão para fazer o logout? Apenas para teste, para que saibamos que tudo está tranquilo:
Button(action: {
// Verificar se há sessionToken storeStringhould only log out if logged in.
if (KeychainWrapper.standard.string(forKey: "Back4Gram.sessionToken") != nil) {
print("Encontrado o sessionToken! Podemos fazer logout.")
// Executar a mutação LogOutUser
apollo.perform(mutation: LogOutUserMutation()){ result in
// Vamos alternar o resultado para que possamos separar um resultado bem-sucedido de um erro
switch result {
// Em caso de sucesso
case .success(let graphQLResult):
// Tentamos Parse nosso resultado
if let result = graphQLResult.data?.users?.logOut {
if (result) {
myMessage.alertTitle = "Yay!"
myMessage.alertText = "O usuário efetuou logout!"
self.showingAlert = true
// Limpar o sessionToken armazenado
let _: Bool = KeychainWrapper.standard.set("", forKey: "Back4Gram.sessionToken")
} else {
myMessage.alertTitle = "Oops!"
myMessage.alertText = "A operação de logout do usuário retornou False."
self.showingAlert = true
}
}
// mas, no caso de qualquer erro do GraphQL, apresentamos essa mensagem
else if let errors = graphQLResult.errors {
// Erros do GraphQL
myMessage.alertTitle = "Oops!"
myMessage.alertText = "Temos um erro de GraphQL: " + errors.description
self.showingAlert = true
print(errors)
}
// Em caso de falha, apresentamos essa mensagem
case .failure(let error):
// Erros de rede ou de formato de resposta
myMessage.alertTitle = "Oops!"
myMessage.alertText = "Ocorreu um erro: " + error.localizedDescription
self.showingAlert = true
print(error)
}
}
} else {
// Erros de formato de rede ou de resposta
myMessage.alertTitle = "Oops!"
myMessage.alertText = "O usuário não parece estar conectado".
self.showingAlert = true
}
}){
Texto ("Logout")
.font(.headline)
.foregroundColor(.white)
.padding()
.frame(largura: 220, altura: 60)
.background(lightBlueColor)
.cornerRadius(15.0)
}
.alert(isPresented: $showingAlert) {
Alert(title: Text(myMessage.alertTitle), message: Text(myMessage.alertText), dismissButton: .default(Text("OK"))))
}
Mais uma vez, vamos testá-lo!
Que legal!
Moveremos o botão Log Out para outro lugar mais tarde, mas, por enquanto, ele funciona para mostrar que o nosso fluxo está funcionando.
Mas o que acontece com o objeto Session agora que saímos do sistema?
Desapareceu automaticamente, como esperado!
Conclusão
Parabéns! Agora você tem as funcionalidades de login e logout implementadas! Além disso, você aprendeu a chamar diferentes mutações, validar resultados e armazenar valores no Keychain! Isso é incrível!
No próximo capítulo, começaremos a trabalhar com várias exibições e a criar nossa exibição principal!
Fique ligado!
Referência
- A parte 1 desta série é Instagram Clone usando Swift UI e GraphQL.
- A parte 2 é Login do Instagram usando Swift UI e GraphQL.
- A parte 3 é Profile View usando Swift UI e GraphQL.
- A parte 4 é a visualização da página inicial do Instagram Clone.
- Baixe um projeto iOS Instagram Clone com código-fonte e comece a usar o Back4App.
Registre-se agora no Back4App e comece a criar seu aplicativo clone do Instagram.

O que é SwiftUI?
SwiftUI é uma nova maneira de criar interfaces de usuário para aplicativos em plataformas Apple. Ela permite que os desenvolvedores determinem a interface do usuário usando o código Swift.
O que é um sessionToken?
O processo de login que estamos desenvolvendo retornará uma string sessionToken. Ela precisa ser protegida. Se conseguirmos manter o sessionToken válido, teremos acesso ao aplicativo; caso contrário, perderemos o acesso. Isso está relacionado à segurança.
O que é o Keychain?
Sabemos que o sessionToken está relacionado à segurança do seu aplicativo. Portanto, ele precisa ser armazenado em um local seguro. Esse local seguro é chamado de keychain. Pode ser um pouco complicado de usar e também pode ser entediante.












