Image may be NSFW.
Clik here to view.Many apps let the user enter information via a form interface. Forms can vary in complexity, from simple username and password screens, to more involved interfaces like those for a contact book or calendar.
Eureka is a powerful library that allows developers to rapidly create form interfaces for user input.
This Eureka tutorial will teach you how to use Eureka’s essential building blocks by crafting the interface of a simple to-do app called EurekaToDo. You’ll see how Eureka makes it easy to set up various commonly-used user interface elements such as date pickers, text fields and segmented controls with no boilerplate UIKit code!
In addition:
- Eureka’s components are very flexible and extensible out-of-the-box and cover the majority of use cases. Eureka makes it easy to implement your custom components if your needs get more specific. Examples of community-generated plugins include a
GooglePlacesRow
and anImageRow
, which you’ll get to use in this Eureka tutorial. - Eureka is a well-documented library with plenty of helpful tips and examples available through its official Github portal.
Getting Started
Download the starter project for EurekaToDo, the to-do list app you’ll be working with for this Eureka tutorial.
In addition to basic view controller transitions, the starter project includes the app’s model and view model layers. Open EurekaToDo.xcworkspace and take a few minutes to browse the project. Here’s a quick overview of important classes:
ToDoListViewController
: Manages the list of to-do items presented to the user.ToDoListViewModel
: The presentation logic forToDoListViewController
.EditToDoItemViewController
: Enables the user to add and edit to-do items — it currently doesn’t do much. All the work for this Eureka tutorial will be in this file.EditToDoItemViewModel
: The presentation logic supportingEditToDoItemViewController
.
You’ll notice the project doesn’t use a storyboard. While Eureka can leverage nib files for custom views, you’ll be amazed by how easy it is to programmatically create and customize common controls.
Note: For this Eureka tutorial, you will be passing values to and from a view model and not the model directly. Although Eureka does not require the use of the Model-View-ViewModel (MVVM) paradigm, MVVM encourages a cleaner, more testable app architecture. For this Eureka tutorial, the view model may be thought of as directly substituting for the app’s model
. See the Further Reading section for more on this topic.
Build and run the application. You’ll see a to-do list pre-populated with a single item. Tapping the item takes you to a blank screen (controlled by EditToDoItemViewController
) with Back and Save navigation items. Tap the Back button in the top left to return to the to-do list.
Image may be NSFW.
Clik here to view.
The edit screen currently leaves a lot to be desired. You probably recall EditToDoItemViewController
didn’t have much in it. This is where you come in! By the time you’re done, the final project’s interface will look like the picture below:
Clik here to view.

Adding Eureka to our View Controller
Open EditToDoItemViewController.swift and replace the current import and class declaration with the following:
import Eureka
import UIKit
class EditToDoItemViewController: FormViewController {
Note: You may need to build the project if the Cocoapod module is not immediately visible to your project.
You’ve imported the Eureka
framework and changed the superclass to be FormViewController
.
FormViewController
is a UIViewController
subclass provided with Eureka. It includes a form
property, which is an instance of Eureka’s Form
class. The Form
class is an abstraction of the UITableView
object into which you’ll be adding various user interface elements.
A Form
instance may contain one or more Section
objects. Each section
, in turn, may contain one or more Row
objects. As you may have guessed from their names, these properties correspond to the sections and rows of the UITableView
. Eureka’s Form
, Section
and Row
abstractions provide some very powerful and flexible functionality.
Adding a Section and a Row
In order to add rows to a form, you will first need a Section
object to contain them.
You’ll use Eureka’s custom +++
operator to add a Section
to the form, and the <<<
operator to add rows to a section. Add the following to viewDidLoad()
, just beneath the call to super
:
//1
form
+++ Section() //2
<<< TextRow() { // 3
$0.title = "Description" //4
$0.placeholder = "e.g. Pick up my laundry"
$0.value = viewModel.title //5
$0.onChange { [unowned self] row in //6
self.viewModel.title = row.value
}
}
Here's a look at what this code does:
- Acts on the
form
object provided byFormViewControler
. - Instantiates and adds a
Section
to the form using Eureka's+++
operator. - Adds a
TextRow
to the section. As you'd expect, this is a row that will contain some text. The initializer accepts a closure used to customize the row's appearance and events. - Adds a
title
andplaceholder
text to the textfield. The title is a left-justified label and the placeholder appears on the right until a value is added. - This sets the initial value of the row to show the to-do item's
title
. - Eureka's
Row
superclass comes with a host of callbacks that correspond to various interaction and view lifecycle events. TheonChange(_ :)
closure is triggered when the row'svalue
property changes. When a change happens, this updates the viewModel'stitle
property to the row's current value.
Build and run the application. When you tap the lone to-do item in the list, the EditToDoItemViewController
screen should now look like the picture below. On the edit screen, tap the item, update the text and then Save. The model object updates with your form input!
Clik here to view.

In only 10 lines of code, you displayed a model-driven textfield in a tableview.
Now that you have an idea of how Eureka works, time to add some other elements!
Setting the Due Date with a Date Picker
Every to-do list needs to have due dates. Fortunately, Eureka has a row type that displays a date picker when tapped. Add the following to the bottom of viewDidLoad()
:
+++ Section()
<<< DateTimeRow() {
$0.dateFormatter = type(of: self).dateFormatter //1
$0.title = "Due date" //2
$0.value = viewModel.dueDate //3
$0.minimumDate = Date() //4
$0.onChange { [unowned self] row in //5
if let date = row.value {
self.viewModel.dueDate = date
}
}
}
You've added another Section, this time with a DateTimeRow
to display the picker. Here's a deeper look at how it's configured:
- To format the presentation of the date, set the row's
dateFormatter
to the staticdateFormatter
- Most Eureka
Row
subclasses allow you to set theirtitle
property to make the purpose of the row clear to the user. - When the row is initially configured, set its value to the view model's due date.
- Use today's date as the minimum date that can be accepted as user input.
- Set the newly-selected date to the view model when
onChange
is triggered.
provided in the starter project.
Build and run the project to confirm the new row is in its own section right below the item title. Tap the row, and a date picker will appear at the bottom of the screen. Note that you can't select a date prior to the present day.
Clik here to view.

Selecting the Repeat Frequency
Any worthwhile to-do item interface should let the user specify whether a task is recurring, and at what interval. You will make use of Eureka's PushRow
class for this. PushRow
accepts an array of options of a given type. Eureka will then take care of generating the supporting interface and navigation to enable the user to make a selection.
Add a PushRow
right below the date picker, in the same section:
<<< PushRow<String>() { //1
$0.title = "Repeats" //2
$0.value = viewModel.repeatFrequency //3
$0.options = viewModel.repeatOptions //4
$0.onChange { [unowned self] row in //5
if let value = row.value {
self.viewModel.repeatFrequency = value
}
}
}
By now, some of the above steps should look a little familiar:
- Add a new
PushRow
to the most-recently instantiated section.PushRow
is a generic class, so you need to specify that you're using it with typeString
in angle brackets. - Again, to make the purpose of this selector clear to the user, set its title to "Repeats".
- Initialize the row's value with the view model's
repeatFrequency
property to show the current selection. - As you might have guessed, the
options
of aPushRow
represent the list of possible values the user can select. Set this toviewModel.repeatOptions
, an array of strings that have been declared in the starter project. If you Command+ClickrepeatOptions
, you'll see the repeat options are:never
,daily
,weekly
,monthly
andannually
. - Whenever the row's value changes, update
viewModel
with the newly-selected value.
Build and run. You'll see that a new row titled Repeats is added to the form.
Clik here to view.

Tapping this row transports you to a view where you can select from the provided options. Upon selection, you're popped back to the root task edit view with your selection reflected.
Clik here to view.

Adding a Priority Selector
A user should be able to specify how important an item is. To do that, you'll use a SegmentedRow
which embeds a UISegmentedControl
into a UITableViewCell
.
Below the code you just added for the PushRow
, add the following:
+++ Section()
<<< SegmentedRow<String>() {
$0.title = "Priority"
$0.value = viewModel.priority
$0.options = viewModel.priorityOptions
$0.onChange { [unowned self] row in
if let value = row.value {
self.viewModel.priority = value
}
}
}
The code above adds a SegmentedRow
with a String
type parameter to the form. By now, the rest of the steps outlined should look familiar. Like the row setup you've seen so far, you're setting the title
, value
, options
and onChange(_:)
properties using the viewModel
.
Build and run. You now have a fully-functioning segmented control to set the item's priority, where "!", "!!" and "!!!" correspond to low-, medium- and high-importance, respectively.
Clik here to view.

Setting a Reminder with an Alert Row
The interface requires a way for the user to select when they will be reminded of an upcoming item, such as "30 minutes before," "1 hour before", or "1 day before". You could use a PushRow
, as in the repeat frequency example. However, to explore the variety of Eureka's components, you will use an alert controller instead. And guess what? Eureka has an AlertRow
for that!
Add the following just below the SegmentedRow
:
<<< AlertRow<String>() {
$0.title = "Reminder"
$0.selectorTitle = "Remind me"
$0.value = viewModel.reminder
$0.options = viewModel.reminderOptions
$0.onChange { [unowned self] row in
if let value = row.value {
self.viewModel.reminder = value
}
}
}
The setup of this row is identical to the rows you have added until this point. However, you also set the additional selectorTitle
property, which is the title of the UIAlertController
presenting the list of options.
Note: Eureka can also display alert controllers with the ActionSheet style using ActionSheetRow
in a similar fashion.
Build and run. When you tap the row titled Reminder, an alert controller is presented, allowing you to select the desired reminder time. You didn't even have to write any UIAlertController
code!
Clik here to view.

Validation
Still on the task edit view, tap-to-edit the description text field. Delete all characters until you can see the placeholder text, then hit Save. Your to-do item no longer has a title!
Clik here to view.

It would be a good idea to make sure the user cannot leave the description blank. Back in viewDidLoad()
, find the code where you added a TextRow
. Add the following just before the closing bracket of the TextRow
closure (and after the onChange
closure):
$0.add(rule: RuleRequired()) //1
$0.validationOptions = .validatesOnChange //2
$0.cellUpdate { (cell, row) in //3
if !row.isValid {
cell.titleLabel?.textColor = .red
}
}
- Initialize and add a
RuleRequired
to theTextRow
object. This is one of the validation rules provided with Eureka to handle required input in a form. It indicates that a value must be provided in the field to pass validation. - Set the row's
validationOptions
to.validatesOnChange
, meaning the validation rule will be evaluated as the row'svalue
changes. - If the value of the row is not valid, set the row's title color to red to red to alert the user.
Note: You can also add custom rules to handle use cases more specific to our needs, as described in Eureka's documentation.
To make sure the user can't leave the editing screen with an invalid entry, replace the contents of the saveButtonPressed(_:)
method with the following:
if form.validate().isEmpty {
_ = navigationController?.popViewController(animated: true)
}
Eureka has a validate()
method that returns an array of any validation errors from all rows with validation rules. If this array is empty, your form has no errors and you can pop the view controller from the navigation stack.
Build and run, and delete the contents of the Description field again. This time, the field label turns red, and the Save button won't allow you to leave until the issue is resolved.
Clik here to view.

Adding More Pizazz with Eureka Plugins
A plugin is a custom Row
component just like any of the rows already included with the Eureka library. You can browse the plugins created by the community at the Eureka Community Portal.
Let's say you wanted the user to be able to attach an image to a to-do item as a visual aid. Sounds like a job for the ImageRow
plugin!
The plugin has already been included in the starter project using the CocoaPods installation instructions found in the plugin readme. To integrate it, start by adding the following import statement to the top of EditToDoItemViewController.swift:
import ImageRow
Note: This plugin requires the addition of the NSCameraUsageDescription
and NSPhotoLibraryUsageDescription
keys in the project's info.plist
file. This has already been done for you in the starter project.
In viewDidLoad()
, add a new section with an ImageRow
to the form:
+++ Section("Picture Attachment")
<<< ImageRow() {
$0.title = "Attachment"
$0.sourceTypes = [.PhotoLibrary, .SavedPhotosAlbum, .Camera] //1
$0.value = viewModel.image //2
$0.clearAction = .yes(style: .destructive) //3
$0.onChange { [unowned self] row in //4
self.viewModel.image = row.value
}
}
Taking it comment-by-comment:
- In the initialization closure, allow the user to select images from their Photo Library, Saved Photos album, or camera if available.
- If an image is already attached to this to-do item, use it to initialize the row's
value
. - Present the "Clear Photo" option with the "destructive" style to indicate that image data may be permanently destroyed when a photo attachment is cleared (when using the camera roll, for example).
- As with the previous examples, update the
viewModel.image
when a new value is set.
Build and run. Tap the row titled Attachment, pick Photo Library from the action sheet, then select an image attachment. The results will be shown in a preview on the Attachment cell.
Image may be NSFW.
Clik here to view.
Creating a Eureka Plugin
Open EditToDoItemViewModel.swift and check out the categoryOptions
array. You can see that the starter project includes possible to-do item categories of Home, Work, Personal, Play and Health. You will create a custom component to allow the user to assign one of these categories to a to-do item.
You will use a Row
subclass that provides the default functionality of a PushRow
but whose layout is more tailored to your needs. Admittedly, this example is a little contrived, but it will help you understand the essentials of crafting your own custom components.
In Xcode's File Navigator, control click the Views group and create a new file named ToDoCategoryRow.swift. Import Eureka at the top of this file:
import Eureka
Until now, you have been dealing almost exclusively with subclasses of Eureka's Row
class. Behind the scenes, the Row
class works together with the Cell class. The Cell class is the actual UITableViewCell
presented on screen. Both a Row
and Cell
must be defined for the same value
type.
Adding a Custom Cell Subclass
You'll start by creating the cell. At the top of ToDoCategoryRow.swift, insert the following:
//1
class ToDoCategoryCell: PushSelectorCell<String> {
//2
lazy var categoryLabel: UILabel = {
let lbl = UILabel()
lbl.textAlignment = .center
return lbl
}()
//3
override func setup() {
height = { 60 }
row.title = nil
super.setup()
selectionStyle = .none
//4
contentView.addSubview(categoryLabel)
categoryLabel.translatesAutoresizingMaskIntoConstraints = false
let margin: CGFloat = 10.0
categoryLabel.heightAnchor.constraint(equalTo: contentView.heightAnchor, constant: -(margin * 2)).isActive = true
categoryLabel.widthAnchor.constraint(equalTo: contentView.widthAnchor, constant: -(margin * 2)).isActive = true
categoryLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
categoryLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
}
//5
override func update() {
row.title = nil
accessoryType = .disclosureIndicator
editingAccessoryType = accessoryType
selectionStyle = row.isDisabled ? .none : .default
categoryLabel.text = row.value
}
}
You've created a custom PushSelectorCell
, which derives from UITableViewCell
and is managed by PushRow
. The cell will display a centered label. Here are some details on how this works:
- You'll be displaying string values in this cell, so you provide
String
as the optional type. - Instantiate the
UILabel
that will be added to the cell. setup()
is called when the cell is initialized. You'll use it to lay out the cell - starting with setting theheight
(provided by a closure),title
andselectionStyle
.- Add the
categoryLabel
and the constraints necessary to center it within the cell'scontentView
. - Override the cell's
update()
method, which is called every time the cell is reloaded. This is where you tell the cell how to present theRow
'svalue
. Note that you're not calling the super implementation here, because you don't want to configure thetextLabel
included with the base class.
Adding a Custom Row Subclass
Below the ToDoCategoryCell
class, add ToDoCategoryRow
:
final class ToDoCategoryRow: _PushRow<ToDoCategoryCell>, RowType { }
Because Row
subclasses are required to be final
, PushRow
cannot be subclassed directly. Instead, subclass the generic _PushRow
provided by Eureka. In the angle brackets, associate the ToDoCategoryRow
with the ToDoCategoryCell
you just created. Finally, every row must adhere to the RowType
protocol.
Now your custom row is all set up and ready to use!
Adding a Dynamic Section Footer
The custom row will be embedded in a "Category" section which will be initially hidden from the user. This section will be unhidden when the user taps a custom table view footer. Open EditToDoItemViewController.swift, and right below the declaration of the dateFormatter
constant, add the following:
let categorySectionTag: String = "add category section"
let categoryRowTag: String = "add category row"
The tag
property is used by the Form
to obtain references to a specific Eureka Row
or Section
. You'll use this constant to tag and later retrieve the section and row used to manage an item's category.
Next, add the following lines at the end of viewDidLoad()
:
//1
+++ Section("Category") {
$0.tag = categorySectionTag
//2
$0.hidden = (self.viewModel.category != nil) ? false : true
}
//3
<<< ToDoCategoryRow() { [unowned self] row in
row.tag = self.categoryRowTag
//4
row.value = self.viewModel.category
//5
row.options = self.viewModel.categoryOptions
//6
row.onChange { [unowned self] row in
self.viewModel.category = row.value
}
}
This adds a new section that includes your custom ToDoCategoryRow
, which is initially hidden. Here are some details:
- Add a section to the form, assigning the
categorySectionTag
constant. - Set the section's
hidden
property totrue
if thecategory
property on the view model is nil. The plain nil-coalescing operator cannot be used here as the hidden property requires a Boolean literal value instead. - Add an instance of
ToDoCategoryRow
to the section tagged withcategoryRowTag
. - Set the row's
value
toviewModel.category
. - Because this row inherits from
PushRow
, you must set the row'soptions
property to the options you want displayed. - As you've seen in prior examples, use the row's
onChange(_:)
callback to update the view model'scategory
property whenever the row'svalue
changes.
Near the top of EditToDoItemViewController
, right below the categorySectionTag
definition, add the following:
lazy var footerTapped: EditToDoTableFooter.TappedClosure = { [weak self] footer in //1
//2
guard let form = self?.form,
let tag = self?.categorySectionTag,
let section = form.sectionBy(tag: tag) else {
return
}
//3
footer.removeFromSuperview()
//4
section.hidden = false
section.evaluateHidden()
//5
if let rowTag = self?.categoryRowTag,
let row = form.rowBy(tag: rowTag) as? ToDoCategoryRow {
//6
let category = self?.viewModel.categoryOptions[0]
self?.viewModel.category = category
row.value = category
row.cell.update()
}
}
EditToDoTableFooter
is a view class included in the starter that contains a button with the title Add Category. It also includes TappedClosure
, a typealias for an action to execute when tapped. The code you added defines a closure of this type that takes a footer
, removes it from the view and displays the category section.
Here is a more detailed look:
- To avoid retain cycles, pass
[weak self]
to the closure. - Safely unwrap references to the view controller and its
form
andcategorySectionTag
properties. You obtain a reference to theSection
instance you defined with thecategorySectionTag
. - When the footer is tapped, remove it from the view since the user shouldn't be allowed to tap it again.
- Unhide the section by setting
hidden
tofalse
then callingevaluateHidden()
.evaluateHidden()
updates the form based on thehidden
flag. - Safely unwrap the reference to the
ToDoCategoryRow
we added to the form. - Ensure the view model's
category
property and the cell's rowvalue
property are defaulted to the first item in the array of options. Call the cell'supdate()
method so its label is refreshed to show the row's value.
The Home Stretch
You're almost at the finish line! At the bottom of viewDidLoad()
, insert the following:
//1
let footer = EditToDoTableFooter(frame: .zero)
//2
footer.action = footerTapped
//3
if let tableView = tableView, viewModel.category == nil {
tableView.tableFooterView = footer
tableView.tableFooterView?.frame = CGRect(x: 0, y: 0, width: tableView.bounds.width, height: 50.0)
}
- Declare an instance of
EditToDoTableFooter
. You pass azero
frame, because the size will be handled by constraints tied to the cell layout. footer.action
is triggered when the footer button is pressed, and this ensures it fires the code you defined in thefooterTapped
closure.- If the view model's
category
is nil, set the table view'stableFooterView
property to our newly-instantiatedfooter
. Next, set the footer's frame to the desired dimensions.
Build and run the project. Tap the large, white button with the words Add Category at the bottom of the table view. Voila! The button is replaced by the custom PushRow
subclass you created.
Tap this row to choose from a selection of "emoji-fied" categories. Eureka!
Clik here to view.

Finishing Touches
The app's users should have the ability to add and delete items. Luckily, the starter project has been set up to do this, and all you have have to do is wire it up.
Open ToDoListViewController.swift and uncomment the following lines of code in addButtonPressed(_:)
:
// Uncomment these lines
//1
let addViewModel = viewModel.addViewModel()
//2
let addVC = EditToDoItemViewController(viewModel: addViewModel)
navigationController?.pushViewController(addVC, animated: true)
addViewModel()
instantiates the view model necessary to add a new to-do item.EditToDoItemViewController
is instantiated with theaddViewModel
just created, then pushed onto the navigation stack.
Build and run. This time tap the + to generate a blank to-do item. Fill in the details, then save it. It’s about time you picked up your laundry!
Image may be NSFW.
Clik here to view.
If you were being adventurous, you might have noticed that tapping Back instead of Save had the same effect: the item was added. This is because the model is created as soon as you tap +.
Next, you're going to work on deletion for this case as well as to delete older items.
In EditToDoItemViewController
, find deleteButtonPressed(_:)
and uncomment the following lines:
//1
let alert = UIAlertController(title: "Delete this item?", message: nil, preferredStyle: .alert)
let cancel = UIAlertAction(title: "Cancel", style: .cancel)
let delete = UIAlertAction(title: "Delete", style: .destructive) { [weak self] _ in
//2
self?.viewModel.delete()
_ = self?.navigationController?.popViewController(animated: true)
}
//3
alert.addAction(delete)
alert.addAction(cancel)
navigationController?.present(alert, animated: true, completion: nil)
The above code will be executed when the Delete button in the navigation bar is pressed.
- Create a
UIAlertController
with a title,cancel
anddelete
actions. - In the completion handler of the
delete
action, tell the view model to delete the to-do item currently being edited. Then pop the current view controller off the navigation stack. - Add the
cancel
anddelete
actions to the alert controller, and present the alert controller on the navigation stack.
Next, delete the following lines of code from the bottom of deleteButtonPressed(_:)
:
// Delete this line
_ = self.navigationController?.popViewController(animated: true)
This is no longer necessary as you're now handling the pop after deleting the model.
And finally, go the initialize()
method and find this line of code:
let deleteButton = UIBarButtonItem(title: "Back", style: .plain, target: self, action: .deleteButtonPressed)
Change the title of the bar button item from "Back" to "Delete" so it reads as follows:
let deleteButton = UIBarButtonItem(title: "Delete", style: .plain, target: self, action: .deleteButtonPressed)
Build and run. Whether you're adding a new item or editing an existing one, tapping Save will take you back to the to-do list only if there are no validation errors (i.e., if the item title is not blank). Tapping Delete will remove the item and take you back to the to-do list.
Clik here to view.

Where To Go From Here?
The finished project can be downloaded here.
I hope this Eureka tutorial helped you gain a broad understanding of the advantages and possibilities of using Eureka. I encourage you to check out Eureka's own excellent documentation and in-depth tutorials to continue your journey:
- Eureka's Github Portal
- Eureka Community Portal
- Creating Your Own Rows For Eureka - Part 1 and Part 2
You can learn more about the Model-View-ViewModel (MVVM) architecture with the following resources:
- Introduction to MVVM
- Tutorial by Erik Cerney from RWDevCon 2016 Vault.
Please share any questions or comments in the discussion below!
The post Eureka Tutorial – Start Building Easy iOS Forms appeared first on Ray Wenderlich.