Trouble with form fields in iOS apps

It’s dead heat what’s more annoying: for customers to input data or for developers to validate and accept that input. It simply can’t be escaped in some cases.

Data models driving the forms are different from one app to another. Form designs tend to be different. Input methods change over time, accessibility is always looming as task you should not forget. On top of that, each app aims to distinguish itself from the pack and thus designers are aiming to infuse the input fields with some life and offer small wins for the end customer using them.

Such a variety and open goals are nightmare for developers. We love stuff that can be fully modeled, rendered using pre-defined rules. And we love to use and re-use existing code without much modifications.

All together, it makes the task of building declarative, easy-to-expand and free-to-augment form libraries next to impossible. iOS world is not lacking in attempts, some better than others.

Eureka has near 10k stars on GitHub. It has neat interactions and supports the largest variety of cells, including ability to add your own custom ones. But it uses a home grown syntax using custom Swift operators which increases learning curve. It has validations built-in which is conceptually wrong in light of my preferred application architecture — validations are defined by business rules, thus they should live in middleware, not UI. That’s a lot of features, which translates to huge dependency when introduced into your project.

Former has an advanced DSL of its own. You need to learn its template syntax in order to build a form. Plus it’s rather old now, layout is built purely in code using AutoLayout’s old visual syntax. Both of those are big turn-offs. However, it has really wide range of pre-built cells and excellent implementation of inline-field editing, including date pickers, multi-value pickers etc.

iZettle’s Form is another huge layout framework based on reactive programming concepts. Forms are just one part of it all; on first glance, I don’t think you can use just the Forms bit without the rest. A framework like this is really not my cup of tea but it deserves to be mentioned if you are into signals and observers.

SwiftForms is another attempt with rather weird approach to styling. It has custom selection handlers that you need to learn but other than that it’s fairly simple to pick up. Not much magic under the hood, which is a plus for me.

SwiftTalk duo built a very modern Form Library based on somewhat advanced usage of Swift generics. Code is rather hard to comprehend unless you are really proficient in Swift. I both like and don’t like how it can create multiple forms, spread out over multiple VCs, using one data-source (they call it state) declaration. Quite smart, if you are willing to extend it further and learn how it works internally so you can actually adapt it to your needs.

Lastly: all of these libraries are using table view cells. I know the reason; self-expanding layouts are quite easy to implement with table rows. It scrolls as much as needed and adapts to input methods. UITableViewController handles the keyboard appearance and scrolling stuff into view, for free.

I have built so many forms in last 5 years, I honestly believe it’s impossible to create a forms library that will work out of box if you care about design consistency across your app. I tried as well. There will always be the need to tweak and adjust, add custom information here and there. For every single library I tried or built, it turned out that I needed to adjust either its behavior or look&feel or both.

Plus, iOS screens are ever expanding and single-column forms may not be enough anymore. Even with single column, you may have some data which are looking better when shown side by side (state / post code / city of an address, for example).

Let’s look at the simplest possible form that you may need in any app: login, with just username and password.

You need something like TextFieldCell, populated using title and optional string value and be done with it. Simple, eh?

Here’s some challenges for you:

I could go on and on, just continue with design requests I had to handle in last 10 years or so. This is wild west territory. The amount of customization points for the model driving such display counts in dozens.

That’s just user/pass using one type of field; imagine what other model data can be shown in text fields: address parts, person names, phone numbers etc. Addresses have things like state/province, zip/post-codes and city names - which can be short (say Richmond, NY 12322) thus it makes sense to show them side-by-side instead of in separate rows. Etc.

That’s just a minor set of possible customizations related to field display. Getting values back and re-connecting them with your model objects is another set of challenges if you want to have a declarative way to setup the form.

I’ve come to believe the right approach is:

=> it can’t be neither a library nor framework.

You start with bare cell/model combination then adjust it to the design at hand. You then build each of your form data sources independently by transforming your actual model objects into TextFieldModel, ToggleModel etc. These are then used to populate their corresponding cells. While this inevitably leads to more work per form you will still be able to fairly quickly build any kind of form, with lots of easy copy/paste.

Over the last year or so, I was slowly building Fields which you can plow however you like (all puns intended). I gave up trying to make it perfect, gave up trying to handle all possible cases out of the box and just wanted to have a solid base to adapt for each and every form I have to build.

Thus the cell design is really, really simple – on purpose. I have field title and the field itself with corresponding value. There’s no hints, explanation, per-field error messages, mandatory glyphs or anything like it. I add them as I need them in each specific implementation. Plus, each field is designed in Interface Builder, making it easy to preview behavior when the cell, as container, is too small or too large. Lastly — entire form uses vertically self-sizing cells, thus you can put as much content inside as you want.

There’ll be more details in upcoming post(s).