Pivotal Engineering Journal

Technical articles from Pivotal engineers.

Building a Native Navigation Menu for iOS with Turbolinks 5

Using Turbolinks 5 to hide your site’s HTML navigation and present a native navigation view to mobile app users.

Posted on by
Categories:   Rails    Turbolinks    iOS   
Edit this post on GitHub.

Background

In the Turbolinks 5 video from Railsconf 2016, we learn that Turbolinks now comes with native iOS and Android adapters. This is great because it gives us an easy way to create apps made mostly of web views, while giving us an escape hatch to leverage native views where we need them. I wanted to see this in action, so I made a very simple Rails app that shows navigation in the HTML for web users, and an iOS app that replaces the HTML navigation links with native controls instead. Here’s a walkthrough of how I did it.

Please Note: While this code totally works, and illustrates a lot of useful concepts that you’ll need to know, I might not be doing things in the most idiomatic way. I’m super new to iOS programming. What you read below is the result of what I’ve figured out how to do, not an endorsement of the best way to do it.

What We Want

We want a web app that shows a navigation menu to visitors coming from web browsers:

But for users who load the site through a mobile app using Turbolinks, we want to render the navigation using native views:

Here’s how we do it…

The Rails App

See the demo code here: https://github.com/pivotal-sg/turbolinks-5-native-navigation-demo/tree/master/rails_app - This is the app in its finished state. Below are the steps you’ll need to follow if you’re modifying an existing app.

First, make sure you follow the Turbolinks installation instructions for Rails.

The Rails app checks for a custom user-agent string to determine if a request came from the iOS app or not. If so, it’ll send the nav info over to the mobile app via JSON.

The server side part of this is straightforward. Inside the Rails app, the important files to look at are (linked for your reference):

  1. app/controllers/application_controller.rb where we define two helpers for our application layout to use.

  2. app/views/layouts/application.html.erb where we set up the conditional navigation so it shows HTML for non-mobile-app requests and sends the nav info via JSON for mobile-app requests.

The iOS App

See the demo code here: https://github.com/pivotal-sg/turbolinks-5-native-navigation-demo/tree/master/ios_app - This is the app in its finished state. Below are the steps you’ll need to follow if you start with the iOS Turbolinks Quickstart guide.

The iOS app uses one main view controller, an instance of UINavigationController to show our web app and a simple UITableView to render our nav menu.

First, follow the Turbolinks for iOS Quick Start guide.

Then, inside the iOS app, the important files to look at are (linked for your reference):

  1. AppDelegate.swift where we remove all the boilerplate stuff from the Quick Start guide (it gets moved into ApplicationController).

  2. ApplicationController.swift where we configure the custom user agent for the Turbolinks client to use and register a listener so that the page can send JSON over to Swift land. Then, we implement the UINavigationController’s willShowViewController delegate method so we can add a nav menu button to the Nav Bar of the Turbolinks VisitableViewController that gets loaded and shown for a page.

  3. NavMenuController where we tell the table view to look at the title property for each nav menu item from the JSON our web app sent, and we trigger ApplicationController’s visit() method when someone taps on a row in our menu.

Wrapping Up & Further Reading

And there you have it. Hopefully you’ve seen how easy it is to wrap your web app into an iOS app with Turbolinks 5, replacing HTML with native controls as needed. If you wanted to take this demo code further, you could make the menu prettier, or animate when it toggles.

Be sure to check out the Turbolinks iOS Demo app. You’ll see how to intercept link clicks to load native views, how to control access to authenticated portions of your web app, and more.