Self Validating Text Fields

My belief is that a text field should be able to do its own validation. Unfortunately, the delegate pattern causes many people to believe they must put the validation logic in a UIViewController, and I don’t blame people for thinking this. I was doing iOS development for well over a year before I finally started to question why.

Previously, I did a post titled UITextField Max Length (The Right Way). That post is similar, and uses some of the same concepts and ideas. But I decided to do another writeup on the topic where I solve a different problem in two different ways. The max length post is great for understanding the nitty gritty theory of what we’re doing, but it doesn’t do too well on how to use the knowledge in a practical manner. So I hope this helps remedy the situation.

Problem Explanation


We’re going to create a text field that only accepts valid positive integers to be entered. We will not allow any kind of negative number, or any type of character. It will also not allow the user to paste invalid data (they can try to paste, but the text will be rejected). This will ensure the text field is always valid.

Validation logic can get complicated fast, but I’d rather focus on how to create a self validating text field. I’m not really interested in doing a writeup of how to decide if your validation logic is good or not. For this reason, our validation logic will be very simple. It will not allow grouping characters (, or . depending on your culture) or negative numbers. Nothing against those things, they just make the validation code more complicated, and that takes focus away from the core of this post.

 

The most important thing to note is that, a text field can not be its own delegate. Considering the whole purpose of this post is to make a text field do its own validation, you would think the goto solution would be “make the text field be its own delegate.” But unfortunately you can’t. If you try to make a text field be its own delegate, it will result in an infinite recursion of  respondsToSelector:. It is entirely Apples code that causes this problem. I guess that means this post should be titled “sort of self validating text fields,” but that’s just a mouth full.

Because we can’t have the text field be its own delegate, we will create an NSObject to encapsulate the delegate logic, and then have that delegate be owned by the text field.1 This gets the logic out of the view controller, and into a reusable object owned by the text field. In the end, we’ll be able to put a QuantityField  on any page, without having to put validation logic in every view controller we can think of.

There are two ways of handling the delegate logic. We can solve the problem with surrogates (wrappers), or blocks. Both of these solutions work by replacing the single delegate of the text field with a delegate chain. The only difference between the solutions is how you insert the validation logic into this new delegate chain.

Surrogates uses some advanced objective-c runtime mechanics to make for less boiler plate code, but Blocks can result in less files overall. Both ways work just fine and neither way is better than the other. It all depends on the person writing code, and the problem being solved. You can decide for yourself which you prefer.

Solution Overview


he normal relationship with a text field and its delegate is illustrated like this.

tf delegates to vc

In this example, the text field delegates certain responsibilities to the view controller. Much like a manager delegates responsibilities to their underlings.

But we are going to create a system like thistf delegate with surrogate

In this system, we the manager delegates certain responsibilities to an underling, and the underling delegates certain responsibilities to the intern. Because the work goes through the underling first, he can decide what to do

  • Send the work to the intern
  • Do some work, and send the rest to the intern
  • Have the intern do some work, but check the results before taking credit from the intern

Now that we have a base knowledge of the system we want to create, we can implement a solution using either a Surrogate or Blocks approach.

Jump to Surrogate

Jump to Block

Surrogate


 

To create the desired system, you’ll want to include this class in your project

BaseSurrogateDelegate.h BaseBlockDelegate.m

The class provided is just boiler plate code. The code was created based on Apple’s documentation. The class provides a foundation for message forwarding. This message forwarding system is what allows the underling to do some stuff, and send the rest to the intern.

The steps we will take to solve the problem are

  • Create a subclass of BaseSurrogateDelegate
    • the validation logic goes in here
  • Create a subclass of UITextField
    • init the subclass of BaseSurrogateDelegate

 

We start off by creating our subclass of BaseSurrogateDelegate

This code should look pretty familiar. We implement  textField:shouldChangeCharactersInRange:replacementString: in much the same way as if we had done so in the view controller. We have an additional delegate so that we can still allow the view controller to implement the text field delegate methods if needed.

So the next thing to do is create the  UITextField subclass to initialize the QuantityDelegate. Remember the QuantityField will hold a strong reference to the QuantityDelegate.

As we can see, the majority of our code is the insanely long init methods. If you’re only ever going to put a QuantityField in a xib/nib or storyboard, you can leave out initWithFrame:. And if you’re only going to initialize in code, you can leave out initWithCoder:. I typically put them both in, just in case I want to do both, but at the same time YAGNI.

With all of that work done, now we can throw a text field into interface builder, and set its class to QuantityField. The view controller can still be the QuantityField’s delegate if desired, and it can rest easy knowing that the QuantityField validates input itself.

If we wanted to use any other validation logic, we would just create a subclass of BaseSurrogateDelegate with the required validation logic, and create a UITextField  with a reference to that subclass.

Block


To create the desired system, you’ll need to throw this class in your project.

BlockDelegate.h BlockDelegate.m

The class is simple. It has properties for you to be able to provide a block implementation for any possible UITextFieldDelegate method. If you don’t provide a block, and the BlockDelegate’s delegate doesn’t implement the selector, the BlockDelegate  claims not to implement the selector. When any selector is called, it will execute the provided block (if there is one), and have its delegate execute its implementation of the UITextFieldDelegate  method.

Looking at the code, you will see a whole lot of boring, boiler plate code.

 

The steps to using this solution are

  • Create a UITextField  subclass
  • Initialize the BlockDelegate
  • Setup the blocks on the BlockDelegate

We can do all of that with a single code example

With all of that work done, now we can throw a text field into interface builder, and set its class to  QuantityField. The view controller can still be the  QuantityField’s delegate if desired, and it can rest easy knowing that the  QuantityField validates input itself.

If we wanted to use any other validation logic, we would just create a different subclass of UITextView with different blocks provided to the BlockDelegate.


  1. Don’t worry, this doesn’t create a retain cycle. The text field retains the delegate, but the delegate doesn’t retain the text field. This really just makes it so the delegate has the same lifecycle as the text field. 

Leave a Comment

Your email address will not be published. Required fields are marked *