Generic UI: Input<T>
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…
- No need to write code to check the type of elements you retrieved from an array, and therefore no need to write error-handling code.
- No need to do any type casting.
- Prevents you from putting something you don’t want into the array.
- You get auto-completion from your IDE.
- Memory optimizations done by the compiler since it knows at compile-time how much memory is needed for your array.
- and more!
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!