Creating Forms in iOS with Eureka
For quite some time now, we’ve been working on a banking iOS application project. We’d been using an old framework for Forms written in Objective-C, so we started to look into newer and more flexible alternatives. We found that Eureka solves our problem very well, but we also needed to create lots of custom elements. In this article, I’d like to share our experience by outlining how we discovered Eureka, and how we wrote the custom elements.
What can we do with the help of this form framework? Eureka’s greatest power lies in the many rows that are already part of the framework. You can use them straight out of the box, and you have a very wide choice.
To mention a few, there are: TextRow, DateRow, CheckRow, various formatted rows for numbers, list rows, etc.
The pictures from Eureka’s repository’s README also speak for themselves.
How it works
The basic building blocks are sections, rows and cells. They help build forms logically and abstract the complexity of UITableViews, which Eureka is obviously built upon. These building blocks keep you focused on the form itself and not the implementation details of the UITableViewDataSource and Delegate.
Sections are equivalent to UITableView sections. They group logical parts of form elements under a common headline. They have a special meaning in selectable sections, which we’ll cover later.
Rows and Cells
Rows can be interpreted as the element of the form, holding the value and the semantics of the item. They can hold a typesafe value, can be disabled or be hidden. They can also have defined custom properties.
Cells are the implementation of what the rows represent. They hold the UI. Many cells are defined by the Eureka framework, but you can define your own. Cells react to changes in the row’s properties.
Each Row has its corresponding Cell, which has defined its type through generics. Generics are actually an essential part of the framework. You can see them if you look into the source code.
For our very basic example, we will be implementing a simple form with name, birthday and a boolean if one likes Eureka. We will also use a button row, which will pre-fill the first 3 fields. This sounds easy — and in fact it is, but gives us a good introduction into writing Eureka Forms.
To get a better idea of this, I made a screenshot of the app we’re building.
The very basic code to build the first section is as follows:
Firstly, notice the class we’re inheriting from: FormViewController. It’s a subclass of UIViewController, not a UITableViewController. Don’t try to use the later one, it won’t work. Eureka will create a UITableView for you, or if you want it not to expand to the whole view, you can add it yourself, and connect the outlet to the FormViewController class’ tableView property.
Form items (rows) are referred to by their tag names, which are strings. It’s good practice to create a struct with constants for them, as they’re then easier to reference them. That’s what the struct FormItems is for in my example.
The sections and rows are added to the form by +++ and <<< respectively. It can be confusing at first sight, but you eventually get used to it — and if you’re like me, you’ll like it how clean it is.
Rows can have their initializer block, where you can set various properties, like, for example the title, placeholder text and value.
Button row and accessing the rows after they’re created
To demonstrate this, I’ll add a section with a button that sets the three rows’ values to match Yoda :) and disable itself while his name is filled.
As we can see, the row has defined a block of what to do when the cell is selected, by calling the method onCellSelection. Two arguments are passed to this block — the cell and the row.
Inside the block, we reference the upper three rows by their tags (look how the constants struct comes handy this time). They’re optionals, so we need to unwrap them. Notice, that we are casting them as? RowOf<Type>. That’s because we don’t really care how they are implemented inside, we just need to know (and operate) on them by their type they’re holding.
Once we set the values, we need to call updateCell() on each row, to update their look. Another thing we do is to set the button cell disabled, while the name holds the value “Yoda”.
Notice, that we don’t specify it as a simple boolean, but instead we pass a block which evaluates if the row is disabled or not. The first parameter is an array that specifies which rows the value is dependent on (by tags). The second is a block which gets the form as a parameter, so that we can operate with it. evaluateDisabled() has to be called to update the button, but this is the only place where we have to do it, because after that it listens to the changes of the cells that the value depends on.
We often need to enable the user to select from multiple values that are offered to them. Three ways to do this, include offering them as input pickers, push row or to let them choose from a so-called selectable section. We’ll show their basic usage, but as with nearly everything in Eureka, you can customize them. There are much more of them, look for them in the Eureka Example app — link at the end.
Input pickers allow the user to select an answer by presenting a picker that shows up in the keyboard area. The code is simple if you want to use the default cell offered by Eureka.
Push rows are presenting the user a new ViewController to pick their choice from. Very suitable for long lists with more than textual information.
Selectable sections are different, in a way that instead of presenting the choices to the user in a separate view or viewcontroller, selectable section presents itself inside the form.
Creating custom form elements
We will create a very simple custom element. It will have two functions — restrict the maximum length of the text and show a tiny progressbar on the bottom to indicate how much we used of that limit.
The code in the viewcontroller is simple enough, so I’ll just show you without any further explanation. We have defined our custom StringLengthRow class that we use here.
I’ll get into more detail with the code for the definition of these custom elements. We define two classes. For the row, we use StringLengthRow and for the cell StringLengthCell.
Let’s begin by StringLengthRow, as this is the simpler one, and the one we’ll be using in our viewcontroller code. Here, we just declare the properties we want to be settable. We also define in the initializer how we want to get the new cell. We chose to load it from nib.
The StringLengthCell is certainly more complex. We have two outlets — valueTextField and valueProgressView wired to the nib. Then we have the 2 basic methods each custom cell implements — setup() and update().
setup() is called once. It is a kind of an initialization function for the cell. Here we set not to highlight the cell when selected and set the delegate for the text field.
update() is called each time when the value is changed (or when updateCell() is called) on the row. It propagates the row’s properties to the cell UI.
Then we have a UITextFieldDelegate method keeping track of the length. We also have the textFieldDidChange(_:) method when the text is being changed. This writes the row’s value from text field and reflects length to the progress view.