I found a number of great articles about associated types and type erasure, but had a really hard time to apply them to my own situation, as most of them deal with Pokemons, Foo/Bar or animals. With that in mind, I figured I should write about my problem and how I managed to solve it.

My situation

I’m currently working on an application that contains a few basic forms. I just started working on the first one, so I have only two kinds of field: a classic text field and a date picker. I started describing my form field with a protocol. The idea was to think about what I needed from a form field: a method I could invoke to validate the content of my form and a value. Because my value would be a String in my TextField and a NSDate for my date picker, I used an associated type.

enum ValidationResult { case Valid case Invalid ( errorMessage : String ) } protocol FormFieldType { associatedtype Type var value : Type ? { get set } func validate () -> ValidationResult }

Concrete types

Once I had my protocol, I could use them on my 2 form fields. Note that I’m not actually validating my fields and always returning .Valid because I want to keep things simple. I used an enum when I could have returned a Bool but that’s because, as I wrote on my company’s blog, I really love enums.

class TextField : UIView , FormFieldType { typealias Type = String var value : String ? func validate () -> ValidationResult { return . Valid } } class DatePicker : UIControl , FormFieldType { typealias Type = NSDate var value : NSDate ? func validate () -> ValidationResult { return . Valid } }

Validation

To validate my form, the idea was to put all my fields in a collection and perform a reduce to end up with a single Bool telling me if my form was valid or not. Even if you haven’t been doing Swift for a long time, you may have aready ran into this error message that doesn’t tell you much.

let firstName = TextField () let lastName = TextField () let date = DatePicker () let fields : [ FormFieldType ] = [ firstName , lastName , date ] // ⚠️ Protocol 'FormFieldType' can only be used as a generic constraint // because it has Self or associated type requirements

In this situation, the compiler is telling you that you can’t create a collection of FormFieldType items because this protocol has an associated type. That’s exactly our sitation here, we are trying to create a collection with 3 elements, 2 of which declared String as their associated type while the last one uses NSDate .

Solution

type erase — Krzysztof Zabłocki (@merowing_) August 19, 2016

Our problem here comes from the associated type and the solution is a very pragmatic one: we need to ignore the associated type because all we care about is the validate method. So, let’s create an object that does just that!

struct AnyField { private let _validate : () -> ValidationResult init < Field : FormFieldType > ( _ field : Field ) { self . _validate = field . validate } func validate () -> ValidationResult { return _validate () } }

One of the great features of Swift is that instances’ methods are actually curried functions. That’s a whole other concept so I would suggest you go and read this article by Ole Begemann. It allows us to keep a reference of the validate method of the FormFieldType and invoke it when we need to. We can now create a Collection of AnyField objects to validate the whole form.

let fields = [ AnyField ( firstName ), AnyField ( lastName ), AnyField ( datePicker )] if fields . reduce ( true , combine : { ( $1 . validate () == . Valid ) && $0 }) { print ( "Form is valid 🎉" ) }

I titled this post with a * because like many concepts out there, the way I solved my problem may not be actual type erasure (but then again, the type has been erased). What is sure is that I now have a way to validate a form by dealing with very simple protocols. Should I decide to take it to the next level and separate my field from the view (and I probably will), it wouldn’t take a lot of work.