效果图:
Views
LoginView.swift:
import SwiftUI
struct LoginView: View {
@EnvironmentObject var authenticationManager: AuthenticationManager
var body: some View {
VStack(spacing: 40) {
Title()
switch authenticationManager.biometryType {
case .faceID:
PrimaryButton(image: "faceid", text: "Login with FaceID")
.onTapGesture {
Task.init {
await authenticationManager.authenticateWithBiometrics()
}
}
case .touchID:
PrimaryButton(image: "touchid", text: "Login with TouchID")
.onTapGesture {
Task.init {
await authenticationManager.authenticateWithBiometrics()
}
}
default:
NavigationLink {
CredentialsLoginView()
.environmentObject(authenticationManager)
} label: {
PrimaryButton(image: "person.fill", text: "Login with your credentials")
}
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(LinearGradient(colors: [.blue, .purple], startPoint: .topLeading, endPoint: .bottomTrailing))
}
}
struct LoginView_Previews: PreviewProvider {
static var previews: some View {
LoginView()
.environmentObject(AuthenticationManager())
}
}
CredentialsLoginView.swift:
import SwiftUI
struct CredentialsLoginView: View {
@EnvironmentObject var authenticationManager: AuthenticationManager
@State private var username = ""
@State private var password = ""
var body: some View {
VStack(spacing: 40) {
Title()
TextField("Username", text: $username)
SecureField("Password", text: $password)
PrimaryButton(showImage: false, text: "Login")
.onTapGesture {
authenticationManager.authenticateWithCredentials(username: username, password: password)
}
}
.textFieldStyle(.roundedBorder)
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(LinearGradient(colors: [.blue, .purple], startPoint: .topLeading, endPoint: .bottomTrailing))
}
}
struct CredentialsLoginView_Previews: PreviewProvider {
static var previews: some View {
CredentialsLoginView()
.environmentObject(AuthenticationManager())
}
}
ContentView.swift:
import SwiftUI
struct ContentView: View {
@StateObject var authenticationManager = AuthenticationManager()
var body: some View {
NavigationView {
VStack {
if authenticationManager.isAuthenticated {
VStack(spacing: 40) {
Title()
Text("Welcome in! You are now authenticated.")
.foregroundColor(.white)
PrimaryButton(showImage: false, text: "Logout")
.onTapGesture {
authenticationManager.logout()
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(LinearGradient(colors: [.blue, .purple], startPoint: .topLeading, endPoint: .bottomTrailing))
} else {
LoginView()
.environmentObject(authenticationManager)
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.edgesIgnoringSafeArea(.all)
.alert(isPresented: $authenticationManager.showAlert) {
Alert(title: Text("Error"), message: Text(authenticationManager.errorDescription ?? "Error trying to login with credentials, please try again"), dismissButton: .default(Text("Ok")))
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Components
Title.swift:
import SwiftUI
struct Title: View {
var body: some View {
Text("Authenticator")
.bold()
.font(.title)
.foregroundColor(.white)
}
}
struct Title_Previews: PreviewProvider {
static var previews: some View {
Title()
.background(LinearGradient(colors: [.blue, .purple], startPoint: .topLeading, endPoint: .bottomTrailing))
}
}
PrimaryButton.swift:
import SwiftUI
struct PrimaryButton: View {
var image: String?
var showImage = true
var text: String
var body: some View {
HStack {
if showImage {
Image(systemName: image ?? "person.fill")
}
Text(text)
}
.padding()
.padding(.horizontal)
.background(.white)
.cornerRadius(30)
.shadow(radius: 10)
}
}
struct PrimaryButton_Previews: PreviewProvider {
static var previews: some View {
PrimaryButton(image: "faceid", text: "Login with FaceID")
}
}
Authentication Manager
AuthenticationManager.swift:
import Foundation
import LocalAuthentication
class AuthenticationManager: ObservableObject {
private(set) var context = LAContext()
@Published private(set) var biometryType: LABiometryType = .none
private(set) var canEvaluatePolicy = false
@Published private(set) var isAuthenticated = false
@Published private(set) var errorDescription: String?
@Published var showAlert = false
init() {
getBiometryType()
}
func getBiometryType() {
canEvaluatePolicy = context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
biometryType = context.biometryType
}
func authenticateWithBiometrics() async {
context = LAContext()
if canEvaluatePolicy {
let reason = "Log into your account"
do {
let success = try await context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason)
if success {
DispatchQueue.main.async {
self.isAuthenticated = true
print("isAuthenticated", self.isAuthenticated)
}
}
} catch {
print(error.localizedDescription)
DispatchQueue.main.async {
self.errorDescription = error.localizedDescription
self.showAlert = true
self.biometryType = .none
}
}
}
}
func authenticateWithCredentials(username: String, password: String) {
if username.lowercased() == "stephanie" && password == "123456" {
isAuthenticated = true
} else {
errorDescription = "Wrong credentials"
showAlert = true
}
}
func logout() {
isAuthenticated = false
}
}
网友评论