User input validation in Xamarin.Forms

Evgeny Zborovsky · April 2, 2018

iOS Android

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!

Twitter, Facebook