Ein Instagram-Klon mit SwiftUI und GraphQL – Anmeldung
In unserem vorherigen Beitrag über die Erstellung einer Instagram-Klon-App haben Sie gelernt, wie Sie alles konfigurieren, um SwiftUI in XCode 11 zum Laufen zu bringen, und eine voll funktionsfähige Sing Up-Ansicht mit GraphQL erstellt.
Heute werden wir lernen, wie man eine Login-Ansicht erstellt und den Benutzer abmeldet.
Dazu benötigen wir das Projekt aus dem vorherigen Beitrag. Wenn Sie also diesen Beitrag nicht verfolgt haben, empfehle ich Ihnen dringend, dies zu tun.
Schnallen Sie sich an und los geht’s!
Um besser zu lernen, können Sie das iOS Instagram Clone Projekt mit Quellcode herunterladen.
Contents
Wollen Sie einen schnellen Start?
Klonen Sie diese App von unserem Hub und fangen Sie an, sie ohne Probleme zu benutzen!
Erstellen der Login-Ansicht
Unsere Login-Ansicht wird unserer SignUp-Ansicht sehr ähnlich sein, eigentlich sogar noch einfacher.
In der logInUser Mutation benötigen wir nur zwei Parameter: Benutzername und Passwort:
Die Abfragen und Mutationen hängen von der Parse-Version ab, die Sie gewählt haben:
Parse 3.7.2:
mutation logInUser($username: String!, $password: String!){ Benutzer{ logIn(benutzername: $benutzername, passwort: $passwort){ sessionToken } } }
Parse 3.8:
Mutation logInUser($Benutzername: String!, $Passwort: String!){ logIn(benutzername: $benutzername, passwort: $passwort){ sessionToken } }
Parse 3.9:
Mutation logInUser($Benutzername: String!, $Passwort: String!){ logIn(benutzername: $benutzername, passwort: $passwort){ sessionToken } }
so dass wir nur diese für unsere Benutzer abfragen müssen.
Fügen wir zunächst eine neue SwiftUI-Ansicht hinzu, indem wir auf Datei > Neu > Datei gehen und unsere SwiftUI-Ansicht auswählen
Wir nennen diese Ansicht LogInView.swift und öffnen sie in unserem Projekt:
Und wie Sie bereits gelernt haben, erstellen Sie Ihren VStack mit den Steuerelementen, die wir benötigen:
- TextField für den Benutzernamen
- SecureField für das Passwort
- Schaltfläche zum Ausführen der Aktion
Um das Design konsistent zu halten, habe ich die Farben, die wir verwenden werden, in die AppDelegate.swift verschoben, also musste ich auch dort SwiftUI importieren:
import SwiftUI let lightGreyColor = Farbe(rot: 239.0/255.0, grün: 243.0/255.0, blau: 244.0/255.0, Deckkraft: 1.0) let lightBlueColor = Farbe(rot: 36,0/255,0, grün: 158,0/255,0, blau: 235,0/255,0, Deckkraft: 1,0)
Denken Sie daran, die Farblinien aus SignUpView.swift und LogInView.swift zu entfernen.
Um die Konsistenz der Steuerelemente beizubehalten, habe ich einfach den Code aus der SignUp-Ansicht kopiert und eingefügt, das E-Mail-Textfeld entfernt und die Textfelder so geändert, dass sie die neuen Funktionalitäten widerspiegeln. Mein Code sah am Ende wie folgt aus:
struct LogInView: View { @State var username: String = "" @State var passwort: String = "" @State private var showingAlert = false var body: some Ansicht { VStack{ Text("Anmelden") .font(.largeTitle) .foregroundColor(lightBlueColor) .fontWeight(.semibold) .padding(.bottom, 20) TextFeld("Benutzername", text: $Benutzername) .padding() .background(hellGrauFarbe) .cornerRadius(5.0) .padding(.bottom, 20) SecureField("Kennwort", text: $kennwort) .padding() .background(hellGrauFarbe) .cornerRadius(5.0) .padding(.bottom, 20) Button(action: { }){ Text("Anmelden!") .font(.headline) .foregroundColor(.white) .padding() .frame(Breite: 220, Höhe: 60) .background(hellblauFarbe) .cornerRadius(15.0) } }.padding() } }
Das war einfach. Mal sehen, wie es aussieht?
Ändern Sie Ihre ContentView.swift, um stattdessen diese Ansicht anzuzeigen:
struct ContentView : Ansicht { var body: some Ansicht { LogInView() } }
Sieht gut aus!
Fügen wir oben unser Logo ein, um es noch ordentlicher zu machen!
Unser Logo wird aus einem Bild bestehen, das wir in unser Projekt integrieren. Ich habe dieses Bild verwendet.
Ziehen Sie das Bild in den Ordner Assets.xcassets in Ihrem Projekt und legen Sie es dort ab:
Es sollte so aussehen:
Von nun an können wir es in unserem Code mit diesem Namen referenzieren: logo-social.
Melden Sie sich jetzt bei Back4App an und beginnen Sie mit der Erstellung Ihrer Instagram-Klon-App.
Unser Logo
Unser Logo wird aus diesem Bild bestehen, aber einfach nur das Bild dorthin zu setzen, würde amateurhaft aussehen. Wir werden es zum Glänzen bringen: kreisförmig, mit einer festen Größe, ein paar Strichen an den Rändern und natürlich einem Schlagschatten wegen… Schlagschatten.
Der Code für all das sieht so aus:
Image("logo-social") .resizable() .aspectRatio(contentMode: .fit) .frame(Breite: 150, Höhe: 150) .clipShape(Kreis()) .overlay(Kreis().stroke(Farbe.blau, lineWidth: 2)) .shadow(Radius: 5) .padding(.bottom, 75)
Und das Ganze kommt oben auf unseren VStack:
var body: some View { VStack{ Image("logo-social") .resizable() .aspectRatio(contentMode: .fit) .frame(Breite: 150, Höhe: 150) .clipShape(Kreis()) .overlay(Kreis().stroke(Farbe.blau, lineWidth: 2)) .shadow(Radius: 5) .padding(.bottom, 75) Text("Anmelden") .font(.largeTitle) .foregroundColor(lightBlueColor) .fontWeight(.semibold) .padding(.bottom, 20) ...
Na, ist das nicht schön?
Der SessionToken
Unser Anmeldevorgang gibt einen sessionToken-String zurück. Wir müssen diesen sessionToken sicher aufbewahren, da er während unserer Operationen verwendet wird: Solange der sessionToken gültig ist, haben wir Zugang zu unserer Anwendung. Wenn er gelöscht oder ungültig wird, werden unsere Aufrufe verweigert.
Da dies direkt mit der Sicherheit zusammenhängt, müssen wir ihn sicher speichern. Der richtige Ort dafür ist der Schlüsselbund unter iOS.
Das Problem mit dem Schlüsselbund ist, dass er schwierig und langweilig zu bedienen ist, daher habe ich mich entschieden, diesen Wrapper zu verwenden. Es macht das Leben so viel einfacher und da wir bereits Cocoapods verwenden, macht es absolut Sinn.
Bearbeiten wir unser Podfile und fügen wir dies zu unseren Pods hinzu:
pod 'SwiftKeychainWrapper'
Dann aktualisieren wir unsere Pods mit dem Befehl
pod installieren
und schließlich öffnen wir unser xcworkspace-Projekt erneut.
Wir sind nun bereit, unsere Keychain-Engine zu verwenden, um…
den SessionToken zu speichern
Laut der Dokumentation unseres neuen Pods wird ein Wert wie folgt im Schlüsselbund gespeichert
KeychainWrapper.standard.set("SomeValue", forKey: "SomeKey")
aber fügen wir noch etwas Logik hinzu.
Beginnen wir mit dem Import unseres Wrappers:
import SwiftKeychainWrapper
Als erstes müssen wir unsere logInUser-Mutation aufrufen, und wenn diese antwortet, speichern wir den sessionToken, falls es einen gibt. Wenn nicht, müssen wir den Benutzer mit einer Warnung benachrichtigen.
Wenn Sie sich an unseren vorherigen Artikel erinnern, haben wir bereits eine Benachrichtigung einschließlich ihrer Struktur kodiert. Lassen Sie uns das wiederverwenden, indem wir den untenstehenden Code aus unserer SingUpView.swift entfernen und ihn an unsere AppDelegate.swift-Datei übergeben, damit alle Ansichten darauf zugreifen können:
struct Message { var alertTitle: String = "" var alertText: String = "" } var myMessage = Nachricht()
Zurück zu unserer Logik: Als erstes müssen wir feststellen, ob der Benutzer die Textfelder für den Benutzernamen und das Passwort ausgefüllt hat. Wenn nicht, gibt es keine Informationen für den Anmeldevorgang und wir müssen den Benutzer darüber informieren.
Unser Aktionscode für die Login-Schaltfläche sollte dies überprüfen. Fügen wir dieses Codestück ein, das die Stringgröße der mit diesen Textfeldern verknüpften Statusvariablen überprüft:
Button(action: { // Prüfen, ob ein Passwort eingegeben wurde if (self.password.count == 0 || self.username.count == 0){ // Wenn nicht, müssen wir die Warnung anzeigen myMessage.alertText = "Sie müssen einen Benutzernamen und ein Passwort angeben." myMessage.alertTitle = "Oops..." self.showingAlert = true } else { // Wenn ja, können wir fortfahren } }){ Text("Anmelden!") .font(.headline) .foregroundColor(.white) .padding() .frame(Breite: 220, Höhe: 60) .background(hellblauFarbe) .cornerRadius(15.0) } .alert(isPresented: $showingAlert) { Alert(title: Text(myMessage.alertTitle), message: Text(myMessage.alertText)), dismissButton: .default(Text("OK"))) }
und testen Sie es…
Schön!
Jetzt müssen wir unsere GraphQL-Mutation aufrufen, um den SessionToken abzurufen und, falls wir einen erhalten, im Schlüsselbund zu speichern.
Sie haben bereits gelernt, wie man Mutationen aufruft, also lassen Sie es uns tun, aber dieses Mal, wenn wir den sessionToken erhalten, werden wir ihn speichern:
// Führen Sie die LogInUser-Mutation durch und übergeben Sie die Parameter, die wir gerade von unseren TextFields erhalten haben apollo.perform(mutation: LogInUserMutation(username: self.username, password: self.password)){ result in // Schalten wir das Ergebnis um, damit wir ein erfolgreiches Ergebnis von einem Fehler unterscheiden können switch ergebnis { // Im Falle eines Erfolgs case .success(let graphQLResult): // Wir versuchen, unser Ergebnis zu parsen if let sessionToken = graphQLResult.data?.users?.logIn.sessionToken { myMessage.alertTitle = "Juhu!" myMessage.alertText = "Benutzer eingeloggt!" self.showingAlert = true print ("Benutzer sessionToken " + sessionToken) // Den SitzungsToken in unseren Schlüsselbund schreiben let _: Bool = KeychainWrapper.standard.set(sessionToken, forKey: "Back4Gram.sessionToken") } // aber im Falle von GraphQL-Fehlern zeigen wir diese Meldung an else if let errors = graphQLResult.errors { // GraphQL-Fehler myMessage.alertTitle = "Ups!" myMessage.alertText = "Wir haben einen GraphQL-Fehler erhalten: " + errors.description self.showingAlert = true print(errors) } // Im Falle eines Fehlers wird die Meldung angezeigt case .failure(let error): // Netzwerk- oder Antwortformatfehler myMessage.alertTitle = "Ups!" myMessage.alertText = "Wir haben einen Fehler erhalten: " + error.localizedDescription self.showingAlert = true print(error) } }
Testen wir es!
Super! Aber woher wissen wir, dass es tatsächlich funktioniert hat?
Wir können das Parse Dashboard unserer App aufrufen und prüfen, ob ein neues Session-Objekt geschrieben wurde:
Wunderbar!
Und wenn wir schon mal hier sind…
Wie wäre es, wenn wir einen Button zum Ausloggen hinzufügen? Nur zum Testen, damit wir wissen, dass alles reibungslos funktioniert:
Button(action: { // Überprüfen Sie, ob ein SessionToken gespeichert istSollte sich nur abmelden, wenn Sie eingeloggt sind. if (KeychainWrapper.standard.string(forKey: "Back4Gram.sessionToken") != nil) { print("SitzungsToken gefunden! Wir können uns abmelden.") // Ausführen der Mutation LogOutUser apollo.perform(mutation: LogOutUserMutation()){ result in // Schalten wir das Ergebnis um, damit wir ein erfolgreiches Ergebnis von einem Fehler unterscheiden können switch result { // Im Falle eines Erfolgs case .success(let graphQLResult): // Wir versuchen, unser Ergebnis zu parsen if let Ergebnis = graphQLResult.data?.users?.logOut { if (Ergebnis) { myMessage.alertTitle = "Juhu!" myMessage.alertText = "Benutzer abgemeldet!" self.showingAlert = true // Löschen des gespeicherten SessionToken let _: Bool = KeychainWrapper.standard.set("", forKey: "Back4Gram.sessionToken") } else { myMessage.alertTitle = "Ups!" myMessage.alertText = "Die Benutzerabmeldung hat einen falschen Wert ergeben." self.showingAlert = true } } // aber im Falle von GraphQL-Fehlern zeigen wir diese Meldung an else if let errors = graphQLResult.errors { // GraphQL-Fehler myMessage.alertTitle = "Ups!" myMessage.alertText = "Wir haben einen GraphQL-Fehler erhalten: " + errors.description self.showingAlert = true print(errors) } // Im Falle eines Fehlers wird die Meldung angezeigt case .failure(let error): // Netzwerk- oder Antwortformatfehler myMessage.alertTitle = "Ups!" myMessage.alertText = "Wir haben einen Fehler erhalten: " + error.localizedDescription self.showingAlert = true print(error) } } } else { // Netzwerk- oder Antwortformatfehler myMessage.alertTitle = "Ups!" myMessage.alertText = "Der Benutzer scheint nicht eingeloggt zu sein." self.showingAlert = true } }){ Text("Abmelden") .font(.headline) .foregroundColor(.white) .padding() .frame(Breite: 220, Höhe: 60) .background(hellblauFarbe) .cornerRadius(15.0) } .alert(isPresented: $showingAlert) { Alert(title: Text(myMessage.alertTitle), message: Text(myMessage.alertText)), dismissButton: .default(Text("OK"))) }
Noch einmal: Testen wir es!
Klasse!
Wir werden die Schaltfläche “Abmelden” später an eine andere Stelle verschieben, aber im Moment funktioniert sie, um zu zeigen, dass unser Ablauf funktioniert.
Aber was ist mit unserem Sitzungsobjekt, nachdem wir uns abgemeldet haben?
Automatisch weg, wie erwartet!
Fazit
Herzlichen Glückwunsch! Sie haben nun die An- und Abmeldefunktionalitäten implementiert! Und nicht nur das, Sie haben auch gelernt, wie man verschiedene Mutationen aufruft, Ergebnisse validiert und Werte im Schlüsselbund speichert! Wie genial ist das denn!
Im nächsten Kapitel werden wir mit mehreren Ansichten arbeiten und mit dem Aufbau unserer Hauptansicht beginnen!
Bleiben Sie dran!
Referenz
- Teil 1 dieser Serie ist Instagram Clone mit Swift UI und GraphQL.
- Teil 2 ist Instagram Login mit Swift UI und GraphQL.
- Teil 3 ist die Profilansicht mit Swift UI und GraphQL.
- Teil 4 ist Instagram Clone Home View.
- Laden Sie ein iOS Instagram Clone Projekt mit Quellcode herunter und beginnen Sie mit Back4App.
Melden Sie sich jetzt bei Back4App an und beginnen Sie mit der Erstellung Ihrer Instagram Clone App.
Was ist SwiftUI?
SwiftUI ist eine neue Möglichkeit, Benutzeroberflächen für Anwendungen auf Apple-Plattformen zu erstellen. Entwickler können damit die Benutzeroberfläche mithilfe von Swift-Code festlegen.
Was ist ein SessionToken?
Der von uns entwickelte Login-Prozess gibt einen SessionToken-String zurück. Dieser muss gesichert werden. Wenn der SessionToken gültig bleibt, haben wir Zugriff auf die Anwendung, andernfalls verlieren wir ihn. Dies hat mit der Sicherheit zu tun.
Was ist ein Schlüsselbund?
Wir wissen, dass das SessionToken mit der Sicherheit Ihrer App zusammenhängt. Es muss daher an einem sicheren Ort gespeichert werden. Dieser sichere Ort wird als Schlüsselbund bezeichnet. Seine Verwendung kann etwas schwierig und auch langweilig sein.