diff --git a/SwiftUIBasics/Views/Components/FormTextField.swift b/SwiftUIBasics/Views/Components/FormTextField.swift index cdd161c..d0deefa 100644 --- a/SwiftUIBasics/Views/Components/FormTextField.swift +++ b/SwiftUIBasics/Views/Components/FormTextField.swift @@ -11,6 +11,7 @@ struct FormTextField: View { var name = "" @Binding var value: String var isSecure = false + var fieldType: UIKeyboardType = .default var body: some View { VStack { @@ -20,6 +21,7 @@ struct FormTextField: View { .padding(.horizontal) } else { TextField(name, text: $value) + .keyboardType(fieldType) .font(.system(size: 20, weight: .semibold, design: .rounded)) .padding(.horizontal) } @@ -33,7 +35,7 @@ struct FormTextField: View { #Preview { VStack(spacing: 20) { - FormTextField(name: "Email", value: .constant(""), isSecure: false) + FormTextField(name: "Email", value: .constant(""), isSecure: false, fieldType: .emailAddress) FormTextField(name: "Password", value: .constant(""), isSecure: true) } } diff --git a/SwiftUIBasics/Views/SignUpView.swift b/SwiftUIBasics/Views/SignUpView.swift index 6cef5d9..0a8ac9b 100644 --- a/SwiftUIBasics/Views/SignUpView.swift +++ b/SwiftUIBasics/Views/SignUpView.swift @@ -9,27 +9,35 @@ import SwiftUI import Combine class SignUpViewModel: ObservableObject { - // inputs - @Published var username: String = "" + + @Published var email: String = "" @Published var password: String = "" @Published var passwordConfirm: String = "" - // outputs - @Published var isValidUsernameLength: Bool = false + + @Published var isValidEmail: Bool = false + @Published var isValidPassword: Bool = false @Published var isValidPasswordLength: Bool = false @Published var isValidPasswordUpperCase: Bool = false + @Published var isValidPasswordLowerCase: Bool = false + @Published var isValidPasswordSymbol: Bool = false + @Published var isValidPasswordNumber: Bool = false @Published var isValidPasswordMatch: Bool = false + @Published var isValidPasswordCase: Bool = false + @Published var isValid: Bool = false private var cancelableSet: Set = [] init() { - $username + $email .receive(on: RunLoop.main) - .map { username in - return username.count >= 4 + .map { email in + let pattern = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}" + let emailPred = NSPredicate(format:"SELF MATCHES %@", pattern) + return emailPred.evaluate(with: email) } - .assign(to: \.isValidUsernameLength, on: self) + .assign(to: \.isValidEmail, on: self) .store(in: &cancelableSet) $password @@ -52,6 +60,46 @@ class SignUpViewModel: ObservableObject { } .assign(to: \.isValidPasswordUpperCase, on: self) .store(in: &cancelableSet) + + $password + .receive(on: RunLoop.main) + .map { password in + let pattern = "[a-z]" + if let _ = password.range(of: pattern, options: .regularExpression) { + return true + } else { + return false + } + } + .assign(to: \.isValidPasswordLowerCase, on: self) + .store(in: &cancelableSet) + + $password + .receive(on: RunLoop.main) + .map { password in + let pattern = /\W+/ + let symbols = Regex(pattern) + if password.contains(symbols) { + return true + } else { + return false + } + } + .assign(to: \.isValidPasswordSymbol, on: self) + .store(in: &cancelableSet) + + $password + .receive(on: RunLoop.main) + .map { password in + let pattern = "[0-9]" + if let _ = password.range(of: pattern, options: .regularExpression) { + return true + } else { + return false + } + } + .assign(to: \.isValidPasswordNumber, on: self) + .store(in: &cancelableSet) Publishers.CombineLatest($password, $passwordConfirm) .receive(on: RunLoop.main) @@ -60,8 +108,16 @@ class SignUpViewModel: ObservableObject { } .assign(to: \.isValidPasswordMatch, on: self) .store(in: &cancelableSet) + + Publishers.CombineLatest4($isValidPasswordUpperCase, $isValidPasswordLowerCase, $isValidPasswordSymbol, $isValidPasswordNumber) + .receive(on: RunLoop.main) + .map { (uppercase, lowercase, symbol, number) in + return uppercase && lowercase && symbol && number + } + .assign(to: \.isValidPasswordCase, on: self) + .store(in: &cancelableSet) - Publishers.CombineLatest4($isValidUsernameLength, $isValidPasswordLength, $isValidPasswordUpperCase, $isValidPasswordMatch) + Publishers.CombineLatest4($isValidEmail, $isValidPasswordLength, $isValidPasswordCase, $isValidPasswordMatch) .map { (a, b, c, d) in return a && b && c && d } @@ -80,13 +136,16 @@ struct SignUpView: View { .bold() .foregroundStyle(.maryBlue) .padding(.bottom, 30) - FormTextField(name: "Username", value: $vm.username) - RequirementText(text: "A minimum of 4 characters", isValid: vm.isValidUsernameLength) + FormTextField(name: "Email", value: $vm.email, fieldType: .emailAddress) + RequirementText(text: "A valid email format", isValid: vm.isValidEmail) .padding() FormTextField(name: "Password", value: $vm.password, isSecure: true) VStack { RequirementText(text: "A minimum of 8 characters", isValid: vm.isValidPasswordLength) - RequirementText(text: "One uppercase letter", isValid: vm.isValidPasswordUpperCase) + RequirementText(text: "At least one uppercase letter", isValid: vm.isValidPasswordUpperCase) + RequirementText(text: "At lest one lowercase letter", isValid: vm.isValidPasswordLowerCase) + RequirementText(text: "At least one symbol", isValid: vm.isValidPasswordSymbol) + RequirementText(text: "At least one digit", isValid: vm.isValidPasswordNumber) } .padding() FormTextField(name: "Confirm Password", value: $vm.passwordConfirm, isSecure: true) @@ -94,8 +153,7 @@ struct SignUpView: View { .padding() .padding(.bottom, 50) Button(action: { - print("Doing") - // Proceed to the next screen + print("Succesfuly") }) { Text("Sign Up") .font(.system(.body, design: .rounded)) @@ -104,7 +162,6 @@ struct SignUpView: View { .padding() .frame(minWidth: 0, maxWidth: .infinity) .background(vm.isValid ? .maryBlue :.turquoise) - // .background(LinearGradient(gradient: Gradient(colors: [.turquoise, .maryBlue]), startPoint: .leading, endPoint: .trailing)) .cornerRadius(10) .padding(.horizontal) } @@ -115,7 +172,6 @@ struct SignUpView: View { .font(.system(.body, design: .rounded)) .bold() Button(action: { - // Proceed to Sign in screen }) { Text("Sign in") .font(.system(.body, design: .rounded)) @@ -132,3 +188,4 @@ struct SignUpView: View { #Preview { SignUpView() } +