Designing mobile APIs – dynamic content

Jonah Williams ·

On mobile devices native UIs offer superior responsiveness and performance but web views offer flexible layouts and data driven content. How can we combine the strengths of both to produce a highly responsive UI which can display dynamic data from a remote server?

On a recent project I needed to present dynamic forms to the app’s users. The number and content of these forms was certain to change over time and it wouldn’t be practical to attempt to distribute an app update every time a form changed. One possible solution would have been to render a html form in a web view. Web views often offer a convenient way to insert server-managed content into an app. However using web views can also have some disadvantages.

It can be difficult to extract user entered data from a web view. This in turn raises the cost of destroying the web view when it is not visible in order to save memory and recreating it on demand. Behavior which may be much easier with native views. The overall memory overhead of loading a web view and its content may also be greater than that of an equivalent native UI.

In my case I also wanted to serve these forms from a generic API which would support multiple clients. While rendering a HTML form would have been convenient it would require at least a unique stylesheet per client in order to match the rest of the client’s design. Even more difficult would have been identifying and supporting different forms of navigation, element selection, and input on different devices. Selecting a list option might be best served by an action sheet with a UIPickerView on iOS devices and a drill-down selection screen which respects the hardware back button on Android devices. Text entry requires different markup to support different keyboard types on different devices. Detecting external keyboards vs onscreen keypads of different sizes may not be possible within a web view so repositioning content to accomodate the keyboard is difficult. Similarly it would be difficult for a client to choose to display a custom keyboard tailored to the type of entry required. Web views also do not have access to all of the native features of a device so a client displaying an HTML form cannot present the user with an option to complete an email input by selecting an address from their contact list.

Given all of those limitation I wanted to push responsibility for displaying these input forms onto the clients who would render an appropriate UI using their native view elements. This introduced its own set of tradeoffs. Native views with custom layout rules can be more expensive to develop than comparable web views. The api implementation would be limited to only those form elements supported by the clients and that list would need to be well defined rather than being able to supply effectively arbitrary HTML. Client side validation would also be more difficult that executing some server provided javascript on form submission.

Ultimately I settled on a Ruby DSL to define a JSON payload representing a form which was sent to the clients by the API’s Rails backend. Using a DSL allowed me to both produce an expressive language for defining these forms as views in my Rails application and also provided a convenient way to enforce that the resulting JSON was limited to a well defined set of possible values the clients would all be able to respond to appropriately. The result was a platform agnostic API response which could be used to generate different UI elements on different clients while returning a well defined set of data back to the API when the form was submitted. It was also flexible enough to allow server side updates to the required input fields and support a growing set of data served by this API without requiring client updates.

A form in one of the Rails views looked something like the following:

data_form do
  fieldset "Identity" do
    field :first_name, :label => "", :placeholder => "First Name"
    field :last_name, :label => "", :placeholder => "Last Name"
  end
  fieldset "Credentials" do
    field :email, :label => "", :placeholder => "Email Address", :type => :email
    field :password, :label => "", :placeholder => "Password", :type => "password"
    field :security_question, :label => "Security Question", :type => :select do
      User::security_questions.each { |q| option q, :label => q }
    end
    field :security_answer, :label => "", :placeholder => "Answer"
  end
  fieldset "Read & Agree" do
    field :terms_of_use, :label => "I agree to the Terms & Conditions.", :type => :checkbox do
      detail UserAgreement.current.terms, :label => "Terms & Conditions", :format => :html
    end
  end
  submit "Submit"
end

On an iOS device that form was then rendered as UINavigationController displaying a grouped UITableView with a section for each fieldset and a cell for each field. Each select field would push an additional table view onto the navigation stack containing a list of the field’s option element. Android clients presented a completely different view of the same form tailored to the device they were running on.

This designed worked well for the application I was working on but it certainly falls somewhere in the middle of a spectrum from completely native UIs to purely web view based UIs and may not be reasonable for many other applications. Taken too far this could grow into a poor reimplementation of HTML. Underutilized and this would be an unnecessarily over-engineered solution. However for a domain which demanded a flexible interface which could be updated from the server, support multiple clients, and provided all the interaction of a native UI I found this to be a great solution. Would you use something similar or what alternatives have you tried for your applications?