Blog

Building a Form Library

Over the last few weeks we have completed a series of ten Swift Talk episodes following the process of building a declarative form library.

For those who haven't been following along, we'd like to take this opportunity to revisit our original design goals and give you an idea of what this library is already capable of.

When we started to build the library, we had two major design goals in mind:

  • Forms should be written with a fully declarative syntax.

  • The library should synchronise the underlying data with the form's UI automatically — a difficult task!

When using forms, we often have to update multiple UI elements in response to a change in the underlying data. This personal hotspot form is an easily recognised example:

Switching the hotspot on or off has a number of effects:

  1. It changes the personal hotspot status label on the main settings screen.

  2. It changes the section footer below the switch.

  3. It shows or hides the section containing the "Network Name" and "Password" fields.

Writing this logic by hand quickly becomes messy, and risks introducing errors.

To create this form with our library, we start by defining the underlying data structures:

								struct Settings {
    var hotspot = Hotspot()

    var hotspotEnabled: String {
        return hotspot.isEnabled ? "On" : "Off"
    }
}

struct Hotspot {
    var isEnabled: Bool = true
    var password: String = "Password"
    var networkName: String = "My Network"
}

							

Now we specify the form's UI using a declarative API that uses Swift's key paths to bind the UI to the data. The hotspot screen is defined like this:

								let hotspotForm: Form<Hotspot> =
    sections([
        section([
            controlCell(title: "Personal Hotspot", control: uiSwitch(keyPath: \.isEnabled))
        ], footer: \.enabledSectionTitle),
        section([
            nestedTextField(title: "Password", keyPath: \.password),
            nestedTextField(title: "Network Name", keyPath: \.networkName)
        ], isVisible: \.isEnabled)
    ])

							

The switch is bound to the isEnabled property, and the section footer below the switch is bound to the computed enabledSectionTitle property, which allows us to make the footer dependent on the hotspot's enabled state. Furthermore, the whole second section is shown or hidden by binding its visibility to the isEnabled property.

The main settings form embeds the hotspot settings like this:

								let settingsForm: Form<Settings> =
    sections([
        section([
            detailTextCell(title: "Personal Hotspot", keyPath: \.hotspotEnabled, form: bind(form: hotspotForm, to: \.hotspot))
        ])
    ])

							

The supplementary label on the right-hand side is bound to the computed hotspotEnabled property, signalling the hotspot's current state on the main screen without having to navigate into the hotspot settings screen.

If you'd like to learn more about this approach, check out the Building a Form Library Swift Talk series — the first three and the last two episodes are publicly available.

The complete series is available for subscribers.

  • Watch the Full Episode

    Building a Form Library

    Introduction

    This episode marks the beginning of a new series where we refactor a hand-coded settings form into a reusable, declarative form library. In this episode, we build the base version and discuss the design goals of the library.

    Episode 94 · March 30, 2018

  • See the Swift Talk Collection

Stay up-to-date with our newsletter or follow us on Twitter .

Back to the Blog

Recent Posts