Un clone di Instagram che utilizza SwiftUI e GraphQL – Login
Nel nostro precedente post su come creare un’app clone di Instagram, abbiamo imparato a configurare tutto per avere SwiftUI funzionante in XCode 11 e abbiamo creato una vista Sing Up perfettamente funzionante con GraphQL.
Oggi impareremo a creare una vista di login e a far uscire l’utente.
Avremo bisogno del progetto del post precedente, quindi se non lo avete seguito, vi consiglio vivamente di farlo.
Allacciate le cinture di sicurezza e partiamo!
Per imparare meglio, scaricate il progetto iOS Instagram Clone con il codice sorgente.
Contents
Volete un inizio rapido?
Clonate questa app dal nostro Hub e iniziate a usarla senza problemi!
Creare la vista di login
La nostra vista Login sarà molto simile alla vista SignUp, anzi è ancora più semplice.
Nella mutazione logInUser abbiamo bisogno solo di due parametri: nome utente e password:
Le query e le mutazioni dipendono dalla versione di Parse scelta:
Parse 3.7.2:
mutazione logInUser($username: String!, $password: String!){ utenti{ logIn(username: $username, password: $password){ sessionToken } } }
Parse 3.8:
mutazione logInUser($username: String!, $password: String!){ logIn(username: $username, password: $password){ sessionToken } }
Parse 3.9:
mutazione logInUser($username: String!, $password: String!){ logIn(nomeutente: $nomeutente, password: $password){ sessionToken } }
quindi dovremo chiedere solo questi per i nostri utenti.
Iniziamo aggiungendo una nuova vista SwiftUI andando su File > New > File e selezionando la nostra vista SwiftUI.
Chiamiamo questa vista LogInView.swift e apriamola nel nostro progetto:
E come avete già imparato, create il vostro VStack con i controlli di cui avremo bisogno:
- TextField per il nome utente
- Campo sicuro per la password
- Pulsante per eseguire l’azione
Per mantenere il design coerente, ho spostato i colori che utilizzeremo in AppDelegate.swift, quindi ho dovuto importare SwiftUI anche lì:
import SwiftUI let lightGreyColor = Color(rosso: 239.0/255.0, verde: 243.0/255.0, blu: 244.0/255.0, opacità: 1.0) let lightBlueColor = Colore(rosso: 36.0/255.0, verde: 158.0/255.0, blu: 235.0/255.0, opacità: 1.0)
Ricordarsi di rimuovere le linee di colore da SignUpView.swift e LogInView.swift.
Inoltre, per mantenere la coerenza dei controlli, ho semplicemente copiato e incollato dalla nostra vista SignUp, rimuovendo il campo di testo dell’e-mail e cambiando i campi di testo per riflettere le nuove funzionalità. Il mio codice è finito così:
struct LogInView: View { @State var username: String = "" @State var password: String = "" @State private var showingAlert = false var body: some View { VStack{ Testo("Accedi") .font(.largeTitle) .foregroundColor(lightBlueColor) .fontWeight(.semibold) .padding(.bottom, 20) Campo di testo("Nome utente", testo: $nomeutente) .padding() .background(lightGreyColor) .cornerRadius(5.0) .padding(.bottom, 20) SecureField("Password", testo: $password) .padding() .background(lightGreyColor) .cornerRadius(5.0) .padding(.bottom, 20) Button(action: { }){ Testo("Accedi!") .font(.headline) .foregroundColor(.white) .padding() .frame(larghezza: 220, altezza: 60) .background(lightBlueColor) .cornerRadius(15.0) } }.padding() } }
È stato semplice. Vediamo come appare?
Modificare il file ContentView.swift per mostrare questa vista:
struct ContentView : View { var body: some View { LogInView() } }
Sembra pulito!
Rendiamolo più ordinato aggiungendo il nostro logo in alto!
Il nostro logo consisterà in un’immagine che integreremo nel nostro progetto. Io ho usato questa.
Trascinate l’immagine nella cartella Assets.xcassets del progetto:
Dovrebbe avere questo aspetto:
D’ora in poi potremo fare riferimento ad essa nel nostro codice con questo nome: logo-social.
Iscrivetevi ora a Back4App e iniziate a costruire la vostra app clone di Instagram.
Il nostro logo
Il nostro logo consisterà in questa immagine, ma metterla semplicemente lì avrebbe un aspetto amatoriale. Lo faremo brillare: circolare, con una dimensione fissa, qualche tratto sui bordi e, ovviamente, drop shadow perché… drop shadow.
Il codice per tutto ciò assomiglia a questo:
Immagine("logo-social") .ridimensionabile() .aspectRatio(contentMode: .fit) .frame(larghezza: 150, altezza: 150) .clipShape(Circle()) .overlay(Circle().stroke(Color.blue, lineWidth: 2)) .shadow(raggio: 5) .padding(.bottom, 75)
E va in cima al nostro VStack:
var body: some View { VStack{ Immagine("logo-social") .resizable() .aspectRatio(contentMode: .fit) .frame(larghezza: 150, altezza: 150) .clipShape(Circle()) .overlay(Circle().stroke(Color.blue, lineWidth: 2)) .shadow(raggio: 5) .padding(.bottom, 75) Testo("Accedi") .font(.largeTitle) .foregroundColor(lightBlueColor) .fontWeight(.semibold) .padding(.bottom, 20) ...
Non è bellissimo?
Il sessionToken
Il nostro processo di login restituirà una stringa sessionToken. Dobbiamo mantenere il sessionToken al sicuro, perché sarà usato durante le nostre operazioni: finché il sessionToken è valido, avremo accesso alla nostra applicazione. Quando viene cancellato o invalidato, le nostre chiamate saranno negate.
Poiché questo è direttamente collegato alla sicurezza, dovremo memorizzarlo in modo sicuro. Il posto giusto per farlo è il Portachiavi di iOS.
L’unico problema del Portachiavi è che è difficile e noioso da usare, quindi ho deciso di usare questo wrapper per gestirlo. Rende la vita molto più facile e, dato che stiamo già usando Cocoapods, ha perfettamente senso.
Modifichiamo il nostro Podfile e aggiungiamo questo ai nostri pod:
pod 'SwiftKeychainWrapper'
quindi aggiorniamo i nostri Pod con il comando
pod install
e infine riapriamo il nostro progetto xcworkspace.
Ora siamo pronti a usare il nostro motore Keychain per…
Memorizzare il sessionToken
Secondo la documentazione del nostro nuovo pod, il modo per salvare un valore nel portachiavi è
KeychainWrapper.standard.set("SomeValue", forKey: "SomeKey")
ma aggiungiamo anche un po’ di logica.
Per iniziare, importiamo il nostro wrapper:
importare SwiftKeychainWrapper
La prima cosa da fare è chiamare la mutazione logInUser e, quando questa risponde, memorizzare il sessionToken, se presente. In caso contrario, dobbiamo notificare all’utente un avviso.
Ora, se ricordate il nostro precedente articolo, abbiamo già codificato un avviso, compresa la sua struttura. Riutilizziamolo rimuovendo il codice sottostante dal nostro SingUpView.swift e passandolo al nostro file AppDelegate.swift, in modo che tutte le viste possano accedervi:
struct Message { var alertTitle: String = "" var alertText: String = "" } var myMessage = Messaggio()
Ora, tornando alla nostra logica, la prima cosa da fare è determinare se l’utente ha riempito le caselle di testo del nome utente e della password. In caso contrario, non ci sono informazioni per il processo di login e dobbiamo notificarlo all’utente.
Il nostro codice d’azione per il pulsante di login deve verificarlo. Aggiungiamo questo pezzo di codice che controlla la dimensione delle stringhe delle variabili di stato collegate ai campi di testo:
Button(action: { // Controlla se è stata digitata una password if (self.password.count == 0 || self.username.count == 0){ // In caso contrario, dobbiamo mostrare l'alert myMessage.alertText = "È necessario fornire un nome utente e una password". myMessage.alertTitle = "Oops..." self.showingAlert = true } else { // Se sì, possiamo procedere } }){ Text("Accedi!") .font(.headline) .foregroundColor(.white) .padding() .frame(larghezza: 220, altezza: 60) .background(lightBlueColor) .cornerRadius(15.0) } .alert(isPresented: $showingAlert) { Alert(title: Text(myMessage.alertTitle), message: Text(myMessage.alertText), dismissButton: .default(Text("OK"))) }
e testarlo…
Ottimo!
Ora dobbiamo chiamare la nostra mutazione GraphQL per recuperare il sessionToken e, se lo otteniamo, memorizzarlo nel Portachiavi.
Abbiamo già imparato a chiamare le mutazioni, quindi facciamolo, ma questa volta se otteniamo il sessionToken, lo memorizziamo:
// Eseguiamo la mutazione LogInUser, passando i parametri appena ottenuti dai nostri campi di testo apollo.perform(mutazione: LogInUserMutation(username: self.username, password: self.password)){ risultato in // Scambiamo il risultato in modo da poter separare un risultato di successo da un errore switch risultato { // In caso di successo case .success(let graphQLResult): // Cerchiamo di Parse il risultato if let sessionToken = graphQLResult.data?.users?.logIn.sessionToken { myMessage.alertTitle = "Evviva!" myMessage.alertText = "L'utente si è registrato!" self.showingAlert = true print ("Utente sessionToken " + sessionToken) // Scrivere il sessionToken nel nostro portachiavi let _: Bool = KeychainWrapper.standard.set(sessionToken, forKey: "Back4Gram.sessionToken") } // ma in caso di errori GraphQL presentiamo questo messaggio else if let errors = graphQLResult.errors { // Errori GraphQL myMessage.alertTitle = "Oops!" myMessage.alertText = "Abbiamo un errore GraphQL: " + errors.description self.showingAlert = true print(errori) } // In caso di fallimento, presentiamo questo messaggio case .failure(let error): // Errori di rete o di formato della risposta myMessage.alertTitle = "Oops!" myMessage.alertText = "Si è verificato un errore: " + error.localizedDescription self.showingAlert = true print(errore) } }
Testiamolo!
Bello! Ma come facciamo a sapere che ha funzionato?
Possiamo andare nella Parse Dashboard della nostra App e verificare se è stato scritto un nuovo oggetto Session:
Come d’incanto!
E visto che siamo qui…
Che ne dite di aggiungere un pulsante per il logout? Solo per fare un test, in modo da sapere che tutto va bene:
Button(action: { // Controlla se c'è un sessionToken storeStringere il logout solo se si è connessi. if (KeychainWrapper.standard.string(forKey: "Back4Gram.sessionToken") != nil) { print("Trovato sessionToken! Possiamo effettuare il logout"). // Eseguire la mutazione LogOutUser apollo.perform(mutazione: LogOutUserMutation()){ risultato in // Scambiamo il risultato in modo da poter separare un successo da un errore switch risultato { // In caso di successo case .success(let graphQLResult): // Cerchiamo di Parse il nostro risultato if let result = graphQLResult.data?.users?.logOut { if (result) { myMessage.alertTitle = "Yay!" myMessage.alertText = "Utente disconnesso!" self.showingAlert = true // Cancellare il sessionToken memorizzato let _: Bool = KeychainWrapper.standard.set("", forKey: "Back4Gram.sessionToken") } else { myMessage.alertTitle = "Oops!" myMessage.alertText = "L'operazione di logout dell'utente ha dato esito negativo". self.showingAlert = true } } // ma in caso di errori GraphQL presentiamo quel messaggio else if let errors = graphQLResult.errors { // Errori GraphQL myMessage.alertTitle = "Oops!" myMessage.alertText = "Abbiamo un errore GraphQL: " + errors.description self.showingAlert = true print(errori) } // In caso di fallimento, presentiamo questo messaggio case .failure(let error): // Errori di rete o di formato della risposta myMessage.alertTitle = "Oops!" myMessage.alertText = "Si è verificato un errore: " + error.localizedDescription self.showingAlert = true print(errore) } } } else { // Errori di rete o di formato della risposta myMessage.alertTitle = "Oops!" myMessage.alertText = "L'utente non sembra essere connesso". self.showingAlert = true } }){ Testo("Esci") .font(.headline) .foregroundColor(.white) .padding() .frame(larghezza: 220, altezza: 60) .background(lightBlueColor) .cornerRadius(15.0) } .alert(isPresented: $showingAlert) { Alert(title: Text(myMessage.alertTitle), message: Text(myMessage.alertText), dismissButton: .default(Text("OK"))) }
Ancora una volta, testiamolo!
Bello!
Sposteremo il pulsante di logout da qualche altra parte più avanti, ma per ora funziona per mostrare che il nostro flusso funziona.
Ma che ne è del nostro oggetto Session, ora che ci siamo disconnessi?
È automaticamente sparito, come previsto!
Conclusione
Congratulazioni! Ora avete implementato le funzionalità di login e logout! Non solo, ma avete imparato a chiamare diverse mutazioni, a convalidare i risultati e a memorizzare i valori nel Portachiavi! Che meraviglia!
Nel prossimo capitolo, inizieremo a lavorare con le viste multiple e a costruire la nostra vista principale!
Restate sintonizzati!
Riferimento
- La prima parte di questa serie è Instagram Clone con Swift UI e GraphQL.
- La parte 2 è Instagram Login con Swift UI e GraphQL.
- Parte 3: Vista del profilo con Swift UI e GraphQL.
- La parte 4 è la vista Home di Instagram Clone.
- Scaricate un progetto iOS Instagram Clone con il codice sorgente e iniziate a usare Back4App.
Registratevi ora a Back4App e iniziate a costruire la vostra app clone di Instagram.
Che cos’è SwiftUI?
SwiftUI è un nuovo modo per creare interfacce utente per le applicazioni sulle piattaforme Apple. Consente agli sviluppatori di determinare l’interfaccia utente utilizzando il codice Swift.
Che cosa è un sessionToken?
Il processo di login che stiamo sviluppando restituirà una stringa sessionToken. Deve essere protetto. Se riusciamo a mantenere valido il sessionToken avremo accesso all’applicazione, altrimenti lo perderemo. È una questione di sicurezza.
Che cosa è un portachiavi?
Sappiamo che sessionToken è correlato alla sicurezza della tua app. Quindi, deve essere conservato in un luogo sicuro. Questo luogo sicuro si chiama portachiavi. Può essere un po’ complicato da usare e può anche risultare noioso.