-
Notifications
You must be signed in to change notification settings - Fork 147
/
Copy pathContentView.swift
109 lines (94 loc) · 3.62 KB
/
ContentView.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import SwiftUI
import sharedKit
struct ContentView: View {
@State private var username: String = ""
@State private var password: String = ""
@ObservedObject var viewModel: ContentView.ViewModel
var body: some View {
VStack(spacing: 15.0) {
ValidatedTextField(titleKey: "Username", secured: false, text: $username, errorMessage: viewModel.formState.usernameError, onChange: {
viewModel.loginDataChanged(username: username, password: password)
})
ValidatedTextField(titleKey: "Password", secured: true, text: $password, errorMessage: viewModel.formState.passwordError, onChange: {
viewModel.loginDataChanged(username: username, password: password)
})
Button("Login") {
viewModel.login(username: username, password: password)
}.disabled(!viewModel.formState.isDataValid || (username.isEmpty && password.isEmpty))
}
.padding(.all)
}
}
struct ValidatedTextField: View {
let titleKey: String
let secured: Bool
@Binding var text: String
let errorMessage: String?
let onChange: () -> ()
@ViewBuilder var textField: some View {
if secured {
SecureField(titleKey, text: $text)
} else {
TextField(titleKey, text: $text)
}
}
var body: some View {
ZStack {
textField
.textFieldStyle(RoundedBorderTextFieldStyle())
.autocapitalization(.none)
.onChange(of: text) { _ in
onChange()
}
if let errorMessage = errorMessage {
HStack {
Spacer()
FieldTextErrorHint(error: errorMessage)
}.padding(.horizontal, 5)
}
}
}
}
struct FieldTextErrorHint: View {
let error: String
@State private var showingAlert = false
var body: some View {
Button(action: { self.showingAlert = true }) {
Image(systemName: "exclamationmark.triangle.fill")
.foregroundColor(.red)
}
.alert(isPresented: $showingAlert) {
Alert(title: Text("Error"), message: Text(error), dismissButton: .default(Text("Got it!")))
}
}
}
extension ContentView {
struct LoginFormState {
let usernameError: String?
let passwordError: String?
var isDataValid: Bool {
get { return usernameError == nil && passwordError == nil }
}
}
class ViewModel: ObservableObject {
@Published var formState = LoginFormState(usernameError: nil, passwordError: nil)
let loginValidator: LoginDataValidator
let loginRepository: LoginRepository
init(loginRepository: LoginRepository, loginValidator: LoginDataValidator) {
self.loginRepository = loginRepository
self.loginValidator = loginValidator
}
func login(username: String, password: String) {
if let result = loginRepository.login(username: username, password: password) as? ResultSuccess {
print("Successful login. Welcome, \(result.data.displayName)")
} else {
print("Error while logging in")
}
}
func loginDataChanged(username: String, password: String) {
formState = LoginFormState(
usernameError: (loginValidator.checkUsername(username: username) as? LoginDataValidator.ResultError)?.message,
passwordError: (loginValidator.checkPassword(password: password) as? LoginDataValidator.ResultError)?.message)
}
}
}