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.