Eureka Tutorial – Start Building Easy iOS Forms
This Eureka tutorial will teach you how Eureka makes it easy to build forms into your iOS app with various commonly-used user interface elements. By Nicholas Sakaimbo.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Eureka Tutorial – Start Building Easy iOS Forms
30 mins
- Getting Started
- Adding Eureka to our View Controller
- Adding a Section and a Row
- Setting the Due Date with a Date Picker
- Selecting the Repeat Frequency
- Adding a Priority Selector
- Setting a Reminder with an Alert Row
- Validation
- Adding More Pizazz with Eureka Plugins
- Creating a Eureka Plugin
- Adding a Custom Cell Subclass
- Adding a Custom Row Subclass
- Adding a Dynamic Section Footer
- The Home Stretch
- Finishing Touches
- Where To Go From Here?
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.
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.
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.
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.
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!
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!
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.
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.
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.
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.