User input validation in Xamarin.Forms

This slideshow requires JavaScript.

One of the very common tasks that any mobile developer meets is validation of the user input. It can be an email, password complexity, length, not empty or any other sort of input validation. In this article we will try to find an appropriate light-weight and reusable solution, so let’s start!

Prerequisites

I am using PropertyChanged.Fody to reduce the INotifyPropertyChanged bloatware related code. Some while ago I wrote a tiny article about it. Beside that, there will be no special requirements, just plain MVVM, C# and Xamarin.Forms.

The solution

Let’s start by introducing a generic interface IValidationRule<T>. It has to have a Validate method which needs to return a bool and a string property to represent the validation description.

public interface IValidationRule<T>
{
    string Description { get; }
    bool Validate(T value);
}

Now let’s create a generic class ValidatableObject<T>. It should have a collection of IValidationRule<T>, bool property to represent the validity of the value, property to represent the value itself, string property to aggregate all the validation descriptions and a constructor that takes all the necessary input.

public class ValidatableObject<T> : BaseViewModel
{
    // Collection of validation rules to apply
    public List<IValidationRule<T>> Validations { get; }
        = new List<IValidationRule<T>>();

 

    // The value itself
    public T Value { get; set; }

// PropertyChanged.Fody will call this method on Value change
    void OnValueChanged() => propertyChangedCallback?.Invoke();


    readonly Action propertyChangedCallback;

    public ValidatableObject(
        Action propertyChangedCallback = null,
        params IValidationRule<T>[] validations)
    {
        this.propertyChangedCallback = propertyChangedCallback;
        foreach (var val in validations)
            Validations.Add(val);
    }

    // PropertyChanged.Fody attribute, on Value change IsValid will change as well
    [DependsOn(nameof(Value))]
    public bool IsValid => Validations.TrueForAll(v => v.Validate(Value));

// Validation descriptions aggregator
    public string ValidationDescriptions =>
        string.Join(Environment.NewLine, Validations.Select(v => v.Description));
}

The code above might be unclear for you if you are not familiar with Fody. For example usage of  OnValueChanged() and usage of DependsOn attribute, luckily the official documentation nicely explains both of them. I also introduced an optional Action propertyChangedCallback which might be useful in case you want to change the button state right after the value change or take some extra action.

Example

public class PasswordValidator : IValidationRule<string>
{
    const int minLength = 6;
    public string Description =>
        $”Password should be at least {minLength} characters long.;
    public bool Validate(string value) =>
        !string.IsNullOrEmpty(value) && value.Length >= minLength;
}

The implementation above takes a string as an input, validates that the string is not null or empty and ensures that the string length is equal or greater than 6.

public class EmailValidator : IValidationRule<string>
{
    const string pattern = @”^(?!\.)(“”([^””\r\\]|\\[“”\r\\])*””|” + @”([-a-z0-9!#$%&’*+/=?^_`{|}~]|(?<!\.)\.)*)(?<!\.)” + @”@[a-z0-9][\w\.-]*[a-z0-9]\.[a-z][a-z\.]*[a-z]$”;
    public string Description => “Please enter a valid email.”;
    public bool Validate(string value)
    {
        if (string.IsNullOrEmpty(value)) return false;
        var regex = new Regex(pattern, RegexOptions.IgnoreCase);
        return regex.IsMatch(value);
    }
}

The implementation above takes a string as an input, validates that the string is not null or empty and ensures that the string represents a valid email address format.

Now lets take a look on a simplified LoginViewModel that has two ValidatableObjects: Email and Password.

public class LoginViewModel : BaseViewModel
{
    public ValidatableObject<string> Email { get; }
    public ValidatableObject<string> Password { get; }
    public ICommand LoginCmd { get; }

    Action propChangedCallBack => (LoginCmd as Command).ChangeCanExecute;

    public LoginViewModel()
    {
        LoginCmd = new Command(
            async () => await Login(),
            () => Email.IsValid && Password.IsValid && !IsBusy);

        Email = new ValidatableObject<string>
            (propChangedCallBack, new EmailValidator());
        Password = new ValidatableObject<string>
            (propChangedCallBack, new PasswordValidator());

    async Task Login() => { /* Login logic */ };
}

LoginCmd will automatically set the button state to enabled / disabled according to validity of the user input. Preceding is done by triggering ChangeCanExecute every time the value of the ValidatableObject is changed.

There is one more thing to demonstrate before switching to XAML and it is our very simple BaseViewModel:

/* PropertyChanged.Fody will take care of the boiler plate code related to INotifyPropertyChanged */
public abstract class BaseViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    // Indicates that the ViewModel is busy
    public bool IsBusy { get; protected set; }
}

Now let’s take a look on our LoginPage:

<ContentPage
    xmlns=http://xamarin.com/schemas/2014/forms
    xmlns:x=http://schemas.microsoft.com/winfx/2009/xaml
    xmlns:converters=clr-namespace:XFFirebaseAuthExample.Converters
    x:Class=XFFirebaseAuthExample.Views.LoginPage>
    <ContentPage.Resources>
        <ResourceDictionary>
            <converters:InvertBooleanConverter x:Key=invertBooleanConverter />

            <Style TargetType=Label x:Key=errorDescriptionStyle>
                <Setter Property=TextColor Value=Red />
                <Setter Property=FontSize Value=Small />
                <Setter Property=HorizontalTextAlignment Value=Center />
            </Style>
        </ResourceDictionary>
    </ContentPage.Resources>
    <StackLayout
        Orientation=Vertical
        Padding=25
        VerticalOptions=Center>
        <Label
            Style={StaticResource errorDescriptionStyle}
            Text={Binding Email.ValidationDescriptions}
            IsVisible={Binding Email.IsValid, Converter={StaticResource invertBooleanConverter}} />
        <Entry
            Placeholder=Email
            Text={Binding Email.Value}>
            <Entry.Triggers>
                <DataTrigger 
                    TargetType=Entry
                    Binding={Binding Email.IsValid}
                    Value=False>
                    <Setter Property=TextColor Value=Red />
                </DataTrigger>
            </Entry.Triggers>
        </Entry>
        <Label
            Style={StaticResource errorDescriptionStyle}
            Text={Binding Password.ValidationDescriptions}
            IsVisible={Binding Password.IsValid, Converter={StaticResource invertBooleanConverter}} />
        <Entry
            IsPassword=true
            Placeholder=Password
            Text={Binding Password.Value} />
        <Button
            Text=Login
            Command={Binding LoginCmd} />
        <ActivityIndicator
            IsVisible={Binding IsBusy}
            IsRunning={Binding IsBusy} />
    </StackLayout>
</ContentPage>

We use InvertBooleanConverter to hide / display the validation error descriptions by binding to IsValid properties of our ValidatableObjects. We also use DataTriggers to set the TextColor property of Entry to red if the input is not valid. Rather then that, there is nothing extraordinary in the XAML above.

The full example is available on github.

Conclusion

User input validation is rather fun than hard. The proposed solution nicely encapsulates the validation logic and can be easily covered by unit tests. One ValidatableObject can have multiple IValidationRules and you can easily decide yourself how to reflect the validity of the input on the UI layer.

P.S. There is a great article on the same topic by David Britch published on blog.xamarin.com. In fact my solution is inspired by Enterprise Application Patterns using Xamarin.Forms a free eBook that I highly recommend for reading!

Advertisements

3 thoughts on “User input validation in Xamarin.Forms

  1. I really like this approach. In the past, I’ve worked on projects where there was a mixture of validation behaviors but it always felt wrong.

    The only issue I have with this is that the form fields are initialized as invalid (red) when a user first views a form, which is not a good UX. I see that you initialize the fields with valid, dummy values, but without those, the user is greeted with error messages. I working on away around that currently but haven’t come up with anything just yet.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s