Generic UI: Input<T>

Generic User Interface

For quite some time I’ve been bothered by the small use of generics in Apple’s iOS libraries. Third party libraries also typically make little use of generics. Generic typing is a fantastic feature of Objective-C and Swift. It allows developers to write code that is incredibly re-usable. The point of generics is really to write less code yet make it even more robust and less prone to errors.

TL;DR GitHub Generic UI

What Are Generics? (or skip this to the next part) #

For those who don’t use statically-typed languages much or just don’t know about generics, the simplest example is that of the array:

class Array<Element> { ... }

In this example the generic type is Element. The person who created the Array class made it so you can customize it for your own use. You can supply – at compile-time – the type of element the array will store. So if you make an Array<String> you will know that the element at index 7 will definitely be a String. And therefore you know that you can access String properties on it, such as the length property array[7].length (not Swift code) before you even run the program, it’s guaranteed to you.

So what’s so great about generics? you could just not have to know the types at compile-time. That’s how it works in Javascript and Python where you don’t know the type of an array’s element until you retrieve it. Some of the benefits are…

Keep in mind the array is a very simple example of a generic class and a lot more can be achieved with generics.

Generic UI in iOS #

How To Make UI Generic? #

UI code is quite different from the rest of the code base in the sense that it has a screen representation. How do you make a visual representation generic? I think the answer is: you don’t. So when it comes to UI, the right way to look at generic programming is to really separate representation from behavior.

When we talk about UI, it can become a little confusing what exactly generic programming is and isn’t for. Generic programming is not going to automatically design a piece of UI, but it will definitely provide you with static typing as well as custom behaviors.

Inputs #

When it comes to UI specifically, I tend to write a lot of boiler plate with my inputs. So I decided to start by turning my input code generic.

Collecting typed data from the user is difficult. We only have a few types of inputs available, for good reason because if Apple provided an input for every kind of data (date, color, decimals, currency…) there may be a few millions. This is where generic programming comes in. My idea is to have an input that can collect <T> from the user, where T could be a Double, a Date, a UIColor or anything else.

I started really small and built a UIActiveInput<Output> class, available on GitHub: Generic UI. It is implemented on top of Apple’s UITextField except it attempts to force the user to only enter the type you want, which is represented as text in the input. Then that string is converted to your generic type on the output. My generic input provides you with a pretty UI but mostly it does let you provide your own transforms and error handling.

let input = UIActiveInput<UInt>()
let output = input.output // <= this is a UInt? :)

Forms #

From inputs the logical next step is forms. Once you’ve painfully built all your inputs to gather someone’s profile information, you want to collect all the data and stick it into a Person class. So I built a UIQuickForm<Output> class that lets you define a model that binds your inputs to properties of a model you provide. See examples here.

Just like with UIActiveInput, I provide you with some UI abstraction that makes your life easier. UIQuickForm takes care of laying out your inputs visually and generates auto-layout code automatically. All you have to do is say where your inputs go and how much space they need to take. Here’s an example:

let form = UIQuickFormView<Person>()

let firstnameInput = UIActiveInput<String>(label: "First Name")
let lastnameInput = UIActiveInput<String>(label: "Last Name")
let ageInput = UIActiveInput<UInt>(label: "Age") // <= notice the Uint?

// Bind the inputs, tell the form where to stick the data and how to handle errors
let firstnameId = form.bind(input: nameInput) { (person, input) in
      person.firstname = input.output ?? ""
}
let lastnameId = form.bind(input: descInput) { (person, input) in
      person.lastname = input.output ?? ""
}
let agetId = form.bind(input: unitCostInput) { (person, input) in
      person.age = input.output ?? 0.0
}

// Tell the form where to put the elements visually
form.addRow([FormElement(firstnameId), FormElement(lastnameId)])
form.addRow([FormElement(agetId)])

// Assemble the form and generate auto-layout code
form.build()

let output = form.output // <= returns a Person?

That’s all! there is no other code you need to write to lay this out, except adding the form as a subview anywhere you like. UIQuickForm is a view so you do have access to all UIView functionality on it, including changing layoutMargins and more.

This is a very simple example but UIQuickForm provides you with a lot more functionality. It lets you say how large some inputs should be relative to others. You may also use your own inputs or third party inputs. You may customize its looks and it fully relays accessibility features for the visually impaired.

#

And there’s a lot more to come!

 
7
Kudos
 
7
Kudos

Now read this

Un-implemented Methods in Swift

A great feature of Swift is that you can require that subclasses of a class implement a method. This can be achieved with required. However, if you’re on the other side wanting to subclass something with no intention to implement all the... Continue →