Det är dags att koda!
I föregående del skapade och konfigurerade vi vårt nya iOS-projekt, bestämde oss för en arkitektur och definierade vår initiala mapp- och filstruktur. Nu ska vi visa MVVM-mönstret i praktiken genom att implementera en enkel inloggningsskärm.
Vi behöver implementera följande filer för att detta ska ske:
Låt oss börja med vyn. Eftersom vi designar en skärm kommer vi att skapa en fil som heter Inloggningsskärm och lägg den i Skärmar mappen i vår inloggningsfunktion. Den ska se ut så här:
import SwiftUI
struct LoginScreen: View {
@ObservedObject var viewModel: LoginViewModel = LoginViewModel()
var body: some View {
VStack {
Spacer()
VStack {
TextField(
"Login.UsernameField.Title".localized,
text: $viewModel.username
)
.autocapitalization(.none)
.disableAutocorrection(true)
.padding(.top, 20)
Divider()
SecureField(
"Login.PasswordField.Title".localized,
text: $viewModel.password
)
.padding(.top, 20)
Divider()
}
Spacer()
Button(
action: viewModel.login,
label: {
Text("Login.LoginButton.Title".localized)
.font(.system(size: 24, weight: .bold, design: .default))
.frame(maxWidth: .infinity, maxHeight: 60)
.foregroundColor(Color.white)
.background(Color.blue)
.cornerRadius(10)
}
)
}
.padding(30)
}
}
struct LoginScreen_Previews: PreviewProvider {
static var previews: some View {
LoginScreen()
}
}Inget särskilt märkvärdigt här; vi skapar bara en vy med två textinmatningar. Den första är för vårt användarnamn och den andra är för lösenordet , vilket är anledningen till att vi använder ett SecureField istället för ett TextField för vår lösenordsinmatning.
Referenserna till vår vymodell är särskilt intressanta. Du kan se att vi skapar en LoginViewModel, som har egenskapsomslutaren ObservedObject . Så om vår vymodell ändras på något sätt (till exempel om värdet på en av dess tillståndsvariabler ändras), kommer LoginScreen att ritas om med de nya värdena. Så enkelt är det. Det är kraften i deklarativ programmering!
Observera att vi inte definierar användarnamns- och lösenordsvariablerna i vår LoginScreen , utan skickar dem till textinmatningsfälten från vår vymodell istället. LoginViewModel hanterar all logik, medan LoginScreen bara visar användargränssnittet och inte gör något annat.
Slutligen, se hur Inloggning knappen är definierad för att anropa viewModel.login funktionen när den trycks ned. Den funktionen kommer också att definieras i vår vymodell.
Eftersom vi har lagt till några nya lokaliserade strängar på vår skärm, låt oss uppdatera vår Lokaliserbara.strängar filen så att den innehåller våra översättningar från English . Vi kan ta bort den gamla ContentView.WelcomeMessage posten som vi angav tidigare eftersom vi inte kommer att använda den längre. Så här kommer våra Localizable.strings att se ut:
"Login.UsernameField.Title" = "Användarnamn"; "Login.PasswordField.Title" = "Lösenord"; "Login.LoginButton.Title" = "Logga in";Nu är det dags att implementera vår vymodell. Skapa en fil som heter Logga inVisaModell i VisaModeller mapp:
import Foundation
class LoginViewModel: ObservableObject {
@Published var username: String = ""
@Published var password: String = ""
func login() {
LoginAction(
parameters: LoginRequest(
username: username,
password: password
)
).call { _ in
// Login successful, navigate to the Home screen
}
}
}Vår vymodell måste vara ett ObservableObject för att LoginScreen ska kunna återges när någon av de publicerade variablerna ändrar sitt tillstånd. Funktionen login() har två huvuduppgifter: att skicka en API-förfrågan till servern via LoginAction och att navigera användaren till startskärmen om begäran lyckas.
Obs: Vi kommer inte att implementera navigeringsdelen för tillfället. För tillfället fokuserar vi på API-förfrågan och återkommer till navigeringen senare!
Vi kan se att våra parametrar kommer att kodas i JSON med hjälp av en struktur som heter InloggningsförfråganSom tur är har Swift ett praktiskt protokoll som heter Kodbar som enkelt kan göra detta åt oss. Vi behöver bara definiera vår förfrågningsmodell. Så, låt oss skapa en ny fil som heter Inloggningsförfrågan och lägg den i Modeller mapp.
import Foundation
struct LoginRequest: Encodable {
let username: String
let password: String
}Kodning av denna struktur skapar följande JSON-begäran-nyttolast:
{
"username": "YOUR_USERNAME",
"password": "YOUR_PASSWORD"
}Bra! Nu ska vi fundera på svaret. När vi får ett svar från servern måste vi avkoda det från JSON till en struktur som vi kan arbeta med. Vi kommer att använda Decodable -protokollet för detta. Låt oss anta att vårt JSON-svar kommer att se ut ungefär så här:
{
"data": {
"accessToken": "YOUR_ACCESS_TOKEN",
"refreshToken": "YOUR_REFRESH_TOKEN"
}
}Nu ska vi skapa en struktur som ska användas för att avkoda detta svar. Skapa en fil som heter Inloggningssvar i Modeller mapp:
import Foundation
struct LoginResponse: Decodable {
let data: LoginResponseData
}
struct LoginResponseData: Decodable {
let accessToken: String
let refreshToken: String
}Här kan du se hur vi enkelt kan placera avkodningsbara strukturer inuti varandra om vi behöver arbeta med kapslad JSON.
Allt som återstår nu är att skapa vår inloggningsåtgärd. Vi kommer inte att använda några tredjepartsbibliotek, som Alamofire, för att skicka våra API-förfrågningar (även om du absolut kan göra det om du vill!), eftersom den gamla goda URLSession från Apple gör jobbet alldeles utmärkt.
Skapa en fil som heter Inloggningsåtgärd och lägg den i Åtgärder mapp. Den ska se ut så här:
import Foundation
struct LoginAction {
var parameters: LoginRequest
func call(completion: @escaping (LoginResponse) -> Void) {
let scheme: String = "https"
let host: String = "base_url"
let path = "/login"
var components = URLComponents()
components.scheme = scheme
components.host = host
components.path = path
guard let url = components.url else {
return
}
var request = URLRequest(url: url)
request.httpMethod = "post"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
do {
request.httpBody = try JSONEncoder().encode(parameters)
} catch {
// Error: Unable to encode request parameters
}
let task = URLSession.shared.dataTask(with: request) { data, _, error in
if let data = data {
let response = try? JSONDecoder().decode(LoginResponse.self, from: data)
if let response = response {
completion(response)
} else {
// Error: Unable to decode response JSON
}
} else {
// Error: API request failed
if let error = error {
print("Error: \(error.localizedDescription)")
}
}
}
task.resume()
}
}Det finns många intressanta saker att se här. Först skapar vi vår URL genom att kombinera tre variabler: schema, värd, och vägDen schema kommer (förhoppningsvis, om du bryr dig om säkerhet) alltid att vara https, kommer vi att sätta våra värd när vi ansluter till en server (nu är det ett hårdkodat dummyvärde bas_url), och den väg är den metod vi använder (i det här fallet, /inloggningDessa variabler genererar följande URL: https://base_url/login för vår begäran.
Härnäst definierar vi våra httpMethod som posta, eftersom vi definierar vår /inloggning som en POST-förfrågan. Du börjar förmodligen märka att många av dessa variabler inte ska hårdkodas i den här enda åtgärden utan skickas som parametrar istället. Oroa dig inte! Vi kommer dit. Ha bara tålamod med mig nu 😉
Vi lägger också till några standardheaders, så att appen vet att den fungerar med JSON. Sedan kodar vi vår begäran till JSON och skickar den till servern med URLSession.shared.dataTask. När svaret är mottaget kommer vi att försöka avkoda det till vår Inloggningssvar struktur. Om allt går som förväntat skickar vi detta avkodade svar i kompletteringshanteraren tillbaka till vår Logga inVisaModellSött! 💪
Om något går fel (JSON-kodning/avkodning misslyckas, det uppstår ett API-förfrågningsfel eller något oväntat händer) kommer vi inte att hamna i vårt kompletteringsblock utan i ett av våra annan felblock istället. För tillfället gör vi inte mycket om det händer. Vi kommer dock att behöva hantera dessa fel senare, eftersom de inträffar mycket oftare än vi skulle vilja!
Det sista vi behöver göra är att faktiskt ansluta vår LoginScreen till vår rotvy, vilket är InnehållsvySå vi behöver modifiera ContentView så att det ser ut så här:
import SwiftUI
struct ContentView: View {
var body: some View {
LoginScreen()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}Om du följde varje steg bör din inloggningsfunktion ha följande mapp- och filstruktur i ditt projekt:

Dags att köra appen! Om du har följt varje steg bör du se något liknande:

Ser bra ut! Vi kan förstås inte logga in ordentligt än eftersom vi inte har något API för att göra det. 😅 Vi lärde oss dock hur man skapar en ny funktion genom att följa MVVM-principerna. Vi skapade vår första UI-skärm, vi kopplade vår skärm till en vymodell och slutligen skapade vi grunden för vår första API-förfrågan.
Det är mycket arbete! 😌
Om du vill ladda ner ett projekt som innehåller allt vi har gjort hittills kan du göra det genom att klicka här .
” Bygg din första SwiftUI-app ” är en del av en artikelserie som är utformad för att hjälpa dig att komma igång med iOS-apputveckling.
Om du har några frågor kring någon av ovanstående tankar, dela dem gärna i kommentarsfältet.
Klicka här för att läsa del 1: Projektuppbyggnad
Klicka här för att läsa del 2: Projektarkitektur
I nästa del av serien kommer vi att sätta upp en (mock)server som hanterar våra API-förfrågningar, så att vi kan implementera och testa inloggningsfunktionen korrekt. Vi ses snart!