19
19
import SwiftUI
20
20
import WireDesign
21
21
22
- public final class PasswordFieldViewModel : ObservableObject {
23
- @Published public private( set) var arePasswordRulesVisible : Bool
24
- @Published public fileprivate( set) var isPasswordValid : Bool
25
- @Published public fileprivate( set) var isPasswordVisible : Bool
26
- @Published public fileprivate( set) var password : String
27
-
28
- fileprivate let passwordValidator : any PasswordValidator
29
-
30
- public init (
31
- arePasswordRulesVisible: Bool = false ,
32
- isPasswordVisible: Bool = false ,
33
- password: String = " " ,
34
- passwordValidator: any PasswordValidator
35
- ) {
36
- self . arePasswordRulesVisible = arePasswordRulesVisible
37
-
38
- self . isPasswordVisible = isPasswordVisible
39
- self . password = password
40
- self . passwordValidator = passwordValidator
41
-
42
- self . isPasswordValid = passwordValidator. validate ( password)
43
- }
44
- }
45
-
46
22
// TODO: [WPB-15571] Add accessibility strings to the mask / unmask buttons
47
23
public struct PasswordField : View {
48
24
@FocusState private var isFocused : Bool
49
- @ObservedObject private var viewModel : PasswordFieldViewModel
50
25
// TextField and SecureField have different heights. Switching between them causes the view to jump.
51
26
// But we also want their height to change with dynamic font sizes. Hence @ScaledMetric.
52
27
@ScaledMetric private var fieldHeight : CGFloat = 48
53
28
29
+ @State public private( set) var arePasswordRulesVisible : Bool
30
+ @State public fileprivate( set) var isPasswordVisible : Bool
31
+ @Binding public fileprivate( set) var isPasswordValid : Bool
32
+ @Binding public fileprivate( set) var password : String
33
+
34
+ private let passwordValidator : any PasswordValidator
54
35
private let placeholder : String
55
36
private let title : String
56
37
57
38
public init (
58
- viewModel: PasswordFieldViewModel ,
39
+ arePasswordRulesVisible: Bool = false ,
40
+ isPasswordVisible: Bool = false ,
41
+ isPasswordValid: Binding < Bool > ,
42
+ password: Binding < String > ,
43
+ passwordValidator: any PasswordValidator ,
59
44
placeholder: String ,
60
45
title: String
61
46
) {
62
- self . viewModel = viewModel
47
+ self . arePasswordRulesVisible = arePasswordRulesVisible
48
+ self . isPasswordVisible = isPasswordVisible
49
+ self . _isPasswordValid = isPasswordValid
50
+ self . _password = password
51
+ self . passwordValidator = passwordValidator
63
52
self . placeholder = placeholder
64
53
self . title = title
65
54
}
@@ -71,21 +60,21 @@ public struct PasswordField: View {
71
60
. foregroundColor ( calculatedColor)
72
61
73
62
HStack {
74
- if viewModel . isPasswordVisible {
75
- TextField ( placeholder, text: $viewModel . password)
63
+ if isPasswordVisible {
64
+ TextField ( placeholder, text: $password)
76
65
. wireTextStyle ( . body1)
77
66
. frame ( height: fieldHeight)
78
67
. focused ( $isFocused)
79
68
} else {
80
- SecureField ( placeholder, text: $viewModel . password)
69
+ SecureField ( placeholder, text: $password)
81
70
. frame ( height: fieldHeight)
82
71
. focused ( $isFocused)
83
72
}
84
73
Spacer ( )
85
74
Button ( action: {
86
- viewModel . isPasswordVisible. toggle ( )
75
+ isPasswordVisible. toggle ( )
87
76
} , label: {
88
- Image ( systemName: viewModel . isPasswordVisible ? " eye " : " eye.slash " )
77
+ Image ( systemName: isPasswordVisible ? " eye " : " eye.slash " )
89
78
. foregroundColor ( . gray)
90
79
} )
91
80
}
@@ -94,27 +83,27 @@ public struct PasswordField: View {
94
83
RoundedRectangle ( cornerRadius: 5 )
95
84
. stroke (
96
85
calculatedColor,
97
- lineWidth: viewModel . password. isEmpty ? 0 : 1
86
+ lineWidth: password. isEmpty ? 0 : 1
98
87
)
99
88
)
100
89
101
- if let passwordRules = viewModel . passwordValidator. localizedRulesDescription,
102
- viewModel . arePasswordRulesVisible {
90
+ if let passwordRules = passwordValidator. localizedRulesDescription,
91
+ arePasswordRulesVisible {
103
92
Text ( passwordRules)
104
93
. font ( . caption)
105
94
. foregroundColor ( calculatedColor)
106
95
}
107
96
}
108
97
. padding ( . horizontal)
109
- . onChange ( of: viewModel . password, perform: { newPassword in
110
- viewModel . isPasswordValid = viewModel . passwordValidator. validate ( newPassword)
98
+ . onChange ( of: password, perform: { newPassword in
99
+ isPasswordValid = passwordValidator. validate ( newPassword)
111
100
} )
112
101
}
113
102
114
103
// MARK: - Helper
115
104
116
105
private var calculatedColor : Color {
117
- switch ( viewModel . password. isEmpty, viewModel . isPasswordValid) {
106
+ switch ( password. isEmpty, isPasswordValid) {
118
107
case ( _, false ) :
119
108
ColorTheme . Base. error. color
120
109
case ( true , _) :
@@ -146,11 +135,10 @@ package struct MockPasswordValidator: PasswordValidator {
146
135
@available ( iOS 17 , * )
147
136
#Preview( " Invalid Password - Hidden " ) {
148
137
PasswordField (
149
- viewModel: PasswordFieldViewModel (
150
- isPasswordVisible: false ,
151
- password: " Invalid password " ,
152
- passwordValidator: MockPasswordValidator ( validationCallback: { _ in false } )
153
- ) ,
138
+ isPasswordVisible: false ,
139
+ isPasswordValid: . constant( false ) ,
140
+ password: . constant( " Invalid password " ) ,
141
+ passwordValidator: MockPasswordValidator ( validationCallback: { _ in false } ) ,
154
142
placeholder: L10n . Passwordtextfield. Preview. placeholder,
155
143
title: L10n . Passwordtextfield. Preview. title
156
144
)
@@ -159,11 +147,10 @@ package struct MockPasswordValidator: PasswordValidator {
159
147
@available ( iOS 17 , * )
160
148
#Preview( " Invalid Password - Visible " ) {
161
149
PasswordField (
162
- viewModel: PasswordFieldViewModel (
163
- isPasswordVisible: true ,
164
- password: " Invalid password " ,
165
- passwordValidator: MockPasswordValidator ( validationCallback: { _ in false } )
166
- ) ,
150
+ isPasswordVisible: true ,
151
+ isPasswordValid: . constant( false ) ,
152
+ password: . constant( " Invalid password " ) ,
153
+ passwordValidator: MockPasswordValidator ( validationCallback: { _ in false } ) ,
167
154
placeholder: L10n . Passwordtextfield. Preview. placeholder,
168
155
title: L10n . Passwordtextfield. Preview. title
169
156
)
@@ -172,11 +159,10 @@ package struct MockPasswordValidator: PasswordValidator {
172
159
@available ( iOS 17 , * )
173
160
#Preview( " Valid Password - Hidden " ) {
174
161
PasswordField (
175
- viewModel: PasswordFieldViewModel (
176
- isPasswordVisible: false ,
177
- password: " Valid password! " ,
178
- passwordValidator: MockPasswordValidator ( validationCallback: { _ in true } )
179
- ) ,
162
+ isPasswordVisible: false ,
163
+ isPasswordValid: . constant( true ) ,
164
+ password: . constant( " Valid password! " ) ,
165
+ passwordValidator: MockPasswordValidator ( validationCallback: { _ in true } ) ,
180
166
placeholder: L10n . Passwordtextfield. Preview. placeholder,
181
167
title: L10n . Passwordtextfield. Preview. title
182
168
)
@@ -185,11 +171,10 @@ package struct MockPasswordValidator: PasswordValidator {
185
171
@available ( iOS 17 , * )
186
172
#Preview( " Valid Password - Visible " ) {
187
173
PasswordField (
188
- viewModel: PasswordFieldViewModel (
189
- isPasswordVisible: true ,
190
- password: " Valid password! " ,
191
- passwordValidator: MockPasswordValidator ( validationCallback: { _ in true } )
192
- ) ,
174
+ isPasswordVisible: true ,
175
+ isPasswordValid: . constant( true ) ,
176
+ password: . constant( " Valid password! " ) ,
177
+ passwordValidator: MockPasswordValidator ( validationCallback: { _ in true } ) ,
193
178
placeholder: L10n . Passwordtextfield. Preview. placeholder,
194
179
title: L10n . Passwordtextfield. Preview. title
195
180
)
0 commit comments