Natively Submitting HTML Forms in iOS

HTML forms are a common means of uploading information to a web server. For example, the HTML 5 specification includes a sample form that simulates a pizza delivery request. The form allows the user to provide contact information, pizza size and topping details, and delivery instructions.

A working implementation of this form can be found on httpbin.org. Submitting the form produces a JSON response that simply echoes the information sent with the request. However, in most "real world" applications, a form submission typically triggers some meaningful action on the server, such as posting a message, responding to a survey, or making a purchase.

Mobile applications often need to perform similar tasks. While it is possible to embed an HTML form in a native application using a web view, this is not always ideal. For example, the form may not be optimized for a mobile device, resulting in controls that are too small and difficult to interact with. Further, embedded forms don't generally provide the seamless experience users expect from a native application. They often look out of place, making it obvious that the app is not truly native:

A form constructed with native controls is usually more visually consistent with platform conventions and much easier to interact with. For example:

However, many native forms are processed via some form of custom XML or JSON-based web service API. This represents a duplication of effort, since developers need to support both the form processing code as well as the code for implementing the web service. It would be ideal if the server-side logic could be shared by both clients, reducing overall development effort while preserving the enhanced user experience provided by the native UI.

MarkupKit

MarkupKit is an open-source framework for simplifying development of native iOS and tvOS applications. It allows developers to construct user interfaces declaratively using a human-readable, HTML-like markup language, rather than visually using Interface Builder or programmatically in code.

For example, the following markup creates an instance of UILabel and sets the value of its text property to "Hello, World!":

<UILabel text="Hello, World!"/>

This markup is equivalent to the following Swift code:

let label = UILabel()
label.text = "Hello, World!"

The native form shown in the previous section was created using the following markup. It declares a static table view whose contents represent the elements of the delivery request form:

<LMTableView style="groupedTableView">
    <!-- Contact information -->
    <LMTableViewCell selectionStyle="none">
        <UITextField id="nameTextField" placeholder="Customer Name"/>
    </LMTableViewCell>

    <LMTableViewCell selectionStyle="none">
        <UITextField id="phoneTextField" placeholder="Telephone" keyboardType="numberPad"/>
    </LMTableViewCell>

    <LMTableViewCell selectionStyle="none">
        <UITextField id="emailTextField" placeholder="Email Address" keyboardType="emailAddress"/>
    </LMTableViewCell>

    <!-- Pizza size/toppings -->
    <?sectionBreak?>
    <?sectionName size?>
    <?sectionSelectionMode singleCheckmark?>

    <sectionHeader title="Pizza Size"/>

    <UITableViewCell textLabel.text="Small" value="small"/>
    <UITableViewCell textLabel.text="Medium" value="medium"/>
    <UITableViewCell textLabel.text="Large" value="large"/>

    <?sectionBreak?>
    <?sectionName toppings?>
    <?sectionSelectionMode multipleCheckmarks?>

    <sectionHeader title="Pizza Toppings"/>

    <UITableViewCell textLabel.text="Bacon" value="bacon"/>
    <UITableViewCell textLabel.text="Extra Cheese" value="cheese"/>
    <UITableViewCell textLabel.text="Onion" value="onion"/>
    <UITableViewCell textLabel.text="Mushroom" value="mushroom"/>

    <!-- Delivery time/instructions -->
    <?sectionBreak?>

    <sectionHeader title="Preferred Delivery Time"/>

    <LMTableViewCell selectionStyle="none">
        <UIDatePicker id="deliveryDatePicker" datePickerMode="time" height="140"/>
    </LMTableViewCell>

    <?sectionBreak?>

    <sectionHeader title="Delivery Instructions"/>

    <LMTableViewCell selectionStyle="none">
        <UITextView id="commentsTextView" height="140" textContainerInset="4" textContainer.lineFragmentPadding="0"/>
    </LMTableViewCell>
</LMTableView>

Of course, MarkupKit isn't the only way to create native forms in iOS; however, it can significantly simplify the task.

HTTP-RPC

HTTP-RPC is an open-source framework for simplifying development of REST applications. It allows developers to access REST-based web services using a convenient, RPC-like metaphor while preserving fundamental REST principles such as statelessness and uniform resource access.

The project currently includes support for consuming web services in Objective-C/Swift and Java (including Android). It provides a consistent, callback-based API that makes it easy to interact with services regardless of target device or operating system.

For example, the following code snippet shows how a Swift client might access a simple web service that returns a friendly greeting:

Swift

serviceProxy.invoke("GET", path: "/hello") { result, error in
    print(result) // Prints "Hello, World!"
}

While HTTP-RPC is often used to access JSON-based REST APIs, it also supports posting data to the server using the application/x-www-form-urlencoded MIME type used by HTML forms.

For example, the following view controller uses the iOS HTTP-RPC client to submit the contents of the form from the previous section to the test service at httpbin.org. The actual form submission is performed in the submit() method using HTTP-RPC's WSWebServiceProxy class:

class ViewController: LMTableViewController {
    @IBOutlet var nameTextField: UITextField!
    @IBOutlet var phoneTextField: UITextField!
    @IBOutlet var emailTextField: UITextField!
    @IBOutlet var deliveryDatePicker: UIDatePicker!
    @IBOutlet var commentsTextView: UITextView!

    override func loadView() {
        view = LMViewBuilder.view(withName: "ViewController", owner: self, root: nil)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        title = "Pizza Delivery Form"

        navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Submit", style: UIBarButtonItemStyle.plain,
            target: self, action: #selector(submit))

        deliveryDatePicker.minuteInterval = 15
    }

    func submit() {
        let timeFormatter = DateFormatter()

        timeFormatter.dateFormat = "hh:mm"

        let serviceProxy = WSWebServiceProxy(session: URLSession.shared, serverURL: URL(string: "https://httpbin.org")!)

        serviceProxy.encoding = WSApplicationXWWWFormURLEncoded

        serviceProxy.invoke("POST", path: "/post", arguments: [
            "custname": nameTextField.text ?? "",
            "custtel": phoneTextField.text ?? "",
            "custemail": emailTextField.text ?? "",
            "size": tableView.value(forSection: tableView.section(withName: "size")) ?? "",
            "topping": tableView.values(forSection: tableView.section(withName: "toppings")),
            "delivery": timeFormatter.string(from: deliveryDatePicker.date),
            "comments": commentsTextView.text
        ]) { result, error in
            let alertController = UIAlertController(title: "Status",
                message: (error == nil) ? "Form submitted." : error!.localizedDescription,
                preferredStyle: .alert)

            alertController.addAction(UIAlertAction(title: "OK", style: .default, handler:nil))

            self.present(alertController, animated: true, completion: nil)
        }
    }
}

While the example controller simply displays a success or failure message in response to the form submission, an actual application might do something slightly more sophisticated, such as presenting a confirmation page returned by the server.

As with MarkupKit, HTTP-RPC isn't strictly required, but its built-in support for executing URL-encoded form posts makes it a good option.

Summary

This article provided an overview of how MarkupKit and HTTP-RPC can be used to natively submit HTML forms in iOS, reducing development effort and improving user experience.

For more information, please see the following:

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s