How is email validation done with swiftUI?
TextField("Please enter your e-mail", text: self.$email)
.modifier(ClearButton(text: $email))
.font(.headline)
.padding(10)
.foregroundColor(.black)
.background(Color.white)
.frame(width: 300, height: 40, alignment: .center)
.cornerRadius(20)
Validation can be done simply using onEditingChanged
of any TextField
.
Below code is for email validation, but it can be used for any other validation.
When user is done and exists the textfield, I validate the text and if it is not validated I remove the text with an error below it.
import SwiftUI
struct ContentView: View {
@State private var emailString : String = ""
@State private var textEmail : String = ""
@State private var isEmailValid : Bool = true
var body: some View {
VStack {
TextField("email...", text: $textEmail, onEditingChanged: { (isChanged) in
if !isChanged {
if self.textFieldValidatorEmail(self.textEmail) {
self.isEmailValid = true
} else {
self.isEmailValid = false
self.textEmail = ""
}
}
})
//.modifier(ClearButton(text: $email))
.font(.headline)
.padding(10)
.foregroundColor(.black)
.background(Color.white)
.frame(width: 300, height: 40, alignment: .center)
.cornerRadius(20)
.autocapitalization(.none)
if !self.isEmailValid {
Text("Email is Not Valid")
.font(.callout)
.foregroundColor(Color.red)
}
}
}
func textFieldValidatorEmail(_ string: String) -> Bool {
if string.count > 100 {
return false
}
let emailFormat = "(?:[\\p{L}0-9!#$%\\&'*+/=?\\^_`{|}~-]+(?:\\.[\\p{L}0-9!#$%\\&'*+/=?\\^_`{|}" + "~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\" + "x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[\\p{L}0-9](?:[a-" + "z0-9-]*[\\p{L}0-9])?\\.)+[\\p{L}0-9](?:[\\p{L}0-9-]*[\\p{L}0-9])?|\\[(?:(?:25[0-5" + "]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-" + "9][0-9]?|[\\p{L}0-9-]*[\\p{L}0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21" + "-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])"
//let emailFormat = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let emailPredicate = NSPredicate(format:"SELF MATCHES %@", emailFormat)
return emailPredicate.evaluate(with: string)
}}
onEditingChanged
: When user tap on this, isChanged = true
, and when user exit and goes somewhere else, isChange = false
.
You need to use combine with SwiftUI for validation
Use TextFieldWithValidator to validate textField
import SwiftUI
import Combine
// MARK: FIELD VALIDATION
@available(iOS 13, *)
public struct FieldChecker {
public var errorMessage:String?
public var valid:Bool {
self.errorMessage == nil
}
public init( errorMessage:String? = nil ) {
self.errorMessage = errorMessage
}
}
@available(iOS 13, *)
public class FieldValidator<T> : ObservableObject where T : Hashable {
public typealias Validator = (T) -> String?
@Binding private var bindValue:T
@Binding private var checker:FieldChecker
@Published public var value:T
{
willSet {
self.doValidate(newValue)
}
didSet {
self.bindValue = self.value
}
}
private let validator:Validator
public var isValid:Bool {
self.checker.valid
}
public var errorMessage:String? {
self.checker.errorMessage
}
public init( _ value:Binding<T>, checker:Binding<FieldChecker>, validator:@escaping Validator ) {
self.validator = validator
self._bindValue = value
self.value = value.wrappedValue
self._checker = checker
}
public func doValidate( _ newValue:T? = nil ) -> Void {
self.checker.errorMessage =
(newValue != nil) ?
self.validator( newValue! ) :
self.validator( self.value )
}
}
// MARK: FORM FIELD
@available(iOS 13, *)
public struct TextFieldWithValidator : View {
// specialize validator for TestField ( T = String )
public typealias Validator = (String) -> String?
var title:String?
var onCommit:() -> Void
@ObservedObject var field:FieldValidator<String>
public init( title:String = "",
value:Binding<String>,
checker:Binding<FieldChecker>,
onCommit: @escaping () -> Void,
validator:@escaping Validator ) {
self.title = title;
self.field = FieldValidator(value, checker:checker, validator:validator )
self.onCommit = onCommit
}
public init( title:String = "", value:Binding<String>, checker:Binding<FieldChecker>, validator:@escaping Validator ) {
self.init( title:title, value:value, checker:checker, onCommit:{}, validator:validator)
}
public var body: some View {
VStack {
TextField( title ?? "", text: $field.value, onCommit: self.onCommit )
.onAppear { // run validation on appear
self.field.doValidate()
}
}
}
}
@available(iOS 13, *)
public struct SecureFieldWithValidator : View {
// specialize validator for TestField ( T = String )
public typealias Validator = (String) -> String?
var title:String?
var onCommit:() -> Void
@ObservedObject var field:FieldValidator<String>
public init( title:String = "",
value:Binding<String>,
checker:Binding<FieldChecker>,
onCommit: @escaping () -> Void,
validator:@escaping Validator ) {
self.title = title;
self.field = FieldValidator(value, checker:checker, validator:validator )
self.onCommit = onCommit
}
public init( title:String = "", value:Binding<String>, checker:Binding<FieldChecker>, validator:@escaping Validator ) {
self.init( title:title, value:value, checker:checker, onCommit:{}, validator:validator)
}
public var body: some View {
VStack {
SecureField( title ?? "", text: $field.value, onCommit: self.onCommit )
.onAppear { // run validation on appear
self.field.doValidate()
}
}
}
}
in your View
import SwiftUI
import Combine
class DataItem: ObservableObject { // observable object
@Published var username:String = "" // observable property
}
struct FormWithValidator : View {
@EnvironmentObject var item:DataItem // data model reference
@State var usernameValid = FieldChecker() // validation state of username field
func username() -> some View {
VStack {
TextFieldWithValidator( title: "username",
value: $item.username,
checker: $usernameValid,
onCommit: submit) { v in
// validation closure where ‘v’ is the current value
if( v.isEmpty ) {
return "username cannot be empty"
}
return nil
}
.padding(.all)
.border( usernameValid.valid ? Color.clear : Color.red )
.background(Color(red: 239.0/255.0, green: 243.0/255.0, blue: 244.0/255.0, opacity: 1.0))
.autocapitalization(.none)
if( !usernameValid.valid ) {
Text( usernameValid.errorMessage ?? "" )
.fontWeight(.light)
.font(.footnote)
.foregroundColor(Color.red)
}
}
}
var isValid:Bool {
usernameValid.valid
}
func submit() {
if( isValid ) {
print( "submit:\nusername:\(self.item.username)")
}
}
var body: some View {
NavigationView {
Form {
Section {
username()
}
Section {
Button( "Submit" ) {
self.submit()
}
.disabled( !self.isValid )
} // end of section
} // end of form
.navigationBarTitle( Text( "Sample Form" ), displayMode: .inline )
} // NavigationView
}
}
#if DEBUG
struct FormVithValidator_Previews: PreviewProvider {
static var previews: some View {
FormWithValidator()
.environmentObject( DataItem() )
}
}
#endif
Credits and inspiration
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With