UPDATED on May 15th: fixed session storage bug, some typos and some small changes according to the 2.0 release of Rubymotion.
Hi all and welcome back in 2013. In the first three tutorials (part one, two and three) I walked you through the developing of a complete Android app backed by a web application in Ruby on Rails and communicating via a JSON API.
With this new series of two tutorials I want to help you develop an iOS app using the same Rails API coded in the first tutorials. The iOS app will use the RubyMotion toolchain that allows to create native app using the Ruby language instead of Objective-C.
RubyMotion isn’t free though, it costs $199.99 (€159,37) for a single license but I can assure you that it’s worth the price if you don’t plan to learn Objective-C or you want to use Ruby to develop apps for the iPhone and the iPad.
In order to start a new project, just open the Terminal in a directory of your choice and write:
If you have read the previous tutorials you already knows what our app should do: send POST and GET HTTP requests with JSON attributes to the register/login authentication endpoint. To do so we’ll need to create some controllers to display the register and login forms.
To start the coding of our Rubymotion app, let’s have a look to the config/make file that will compile and launch the app in the iOS simulator: the Rakefile. If you need some more information on what rake is and can do, go to the official documentation.
A small change to the Authentication API
Before we can start, we have to make some changes in the Ruby on Rails application in order to remove the session caching using Warden. We must do so because iOS uses the sessions (if they’re present in the HTTP response headers) and it messes up the authentication with the API (ie: you still keep the same authenticated user even if you logout and login with another one).
To avoid this, edit the Devise initializer and add the :token_auth to the skip_session_storage array and add :store => false to the warden.authenticate! parameters used in the create and destoy methods in the Api::V1::SessionsController and the sign_in method call in the API::V1::RegistrationsController:
The Rakefile: configuration and dependencies
I will use some cool features provided by BubbleWrap like the App::Persistence helper that wraps NSUserDefaults, BW::JSON for JSON encoding and parsing and BW::HTTP that wraps NSURLRequest, NSURLConnection in order to communicate with our Rails API. BubbleWrap provides a ruby-like interface to common Cocoa and iOS APIs: go and check out the documentation if you want learn some more.
To use BubbleWrap just open the Terminal and install the bundler gem:
Then create a new file called Gemfile in the root directory of the project.
And finally use Bundler to install the gem(s) you specify in the Gemfile:
Let’s now setup the RubyMotion app’s Rakefile with all our dependencies and configurations:
These few lines should be auto-explicatory, but they basically tells to our building system what are the dependencies and the basic configuration for our app. You can find more information on the official ‘hello motion’ tutorial.
The app’s starting point, the App Delegate
The core of every iOS app is the App Delegate: it’s a custom object created at app launch time with the primary job of handling state transitions within the app. More information on the iOS application architecture can be found on the official documentation.
In this simple app we just define the application:didFinishLaunchingWithOptions and an auxiliary method that shows a modal window with the Welcome view.
The method application:didFinishLaunchingWithOptions creates a new window from the class UIWindow to contain the main UINavigationController in which the TaskListController is pushed on top of the stack and set it as the root controller of the window.
It then checks if there’s an authToken key already present within the App::Persistence interface: if it’s not there that means that the user isn’t logged in yet and so the welcome screen with the choice between login and register has to be diplayed, calling the showWelcomeController method defined below.
The first controller
This is the main entry point of the application: it’s a simple controller that will show just a text label and two button, each one pushing another controller on the stack.
I used some simple geometry trick to place te buttons accordingly to the phone screen’s dimensions, nothing fancy. You can see the end result after the block of code.
There are just two extra methods that will be called when the user touch the buttons: each one of them creates a new controller for the user registration and login and pushes it on the stack of its own UINavigationController.
Create a new user account and log users in
Until now we covered some pretty basic stuff. It’s time to do some more and delve into the cool aspects of Rubymotion.
In order to ease the creation of interfaces with user input forms, I will use Formotion by the great Clay Allsopp: it provides a simple “ruby-fu” approach to populate your forms with every type of inputs iOS could provide.
To install it, add the Formotion gem to the Gemfile and open the Terminal and install the Formotion gem.
The first controller we are going to create is the RegisterController: it displays the mandatory fields for the creation of a new user and sends them to the API authentication endpoint to validate them and return a valid authentication token.
The code should be self explanatory, but just to have an overview the entry point is the init method: a new Formotion::Form is created with a dictonary defining the structure of the fields. The method called when the user submits the form is added to the on_submit block and the form is finally initialized.
The other important method of this controller is the register one that is called on submit. As you can see it sets the correct content type in the HTTP headers and compile te JSON object containing the value from the fields using the form.render[:name_of_the_field] method.
Before actually sending the JSON request to the API, we check if all the required fields have been compiled and if the password and the password confirmation are the same, displaying an alert informing the user if they are not.
If everything is fine, it’s time to use the BubbleWrap’s BW::HTTP.post method to send the HTTP request to the Rails application: before doing so though I will use SVProgressHUD in order to show a nice “loading” alert to give some feedback to the user. The SVProgressHUD is a simple Cocoa library that we actually added using (Motion) CocoaPods in the Rakefile. More info could be found in the Motion CocoaPods documentation.
Inside the BW::HTTP.post block we check if the response is ok or ko, displaying an alert iforming the user if something went wrong. If the response is ok, we use the BW::JSON.parse method to parse the response and retrieve the auth_token for the brand new user, saving it in the persistence key-value store.
Finally the whole navigation controller that contains this and the WelcomeController is dismissed and the TasksListController.controller.refresh is called to actually retrieve the user’s tasks. More infor about that later.
The LoginController is actually really similar to the register one. It uses Formotion to setup the form fields and the login method sends the data to the JSON API, providing feedback if something has gone wrong or the credentials aren’t valid.
The most important thing is that - if the authentication with the backend is succesful - we save the auth_token contained in the API response in the App::Persistence store.
The RegisterController and LoginController
Finally we come to the main view of our app: the Tasks list. First though, we need to setup a simple object to store our tasks retrieved from the API and interact with them within our iOS application.
For this first part of the tutorial, the TaskController is just a placeholder for the completion of the authentication process and that will hold the functional logout button, more will be added in the next part.
In order to make the logout work, we need to add the logout method to the AppDelegate class: it uses the BW::HTTP.delete method to send a reset of the authentication token to the API and deleting the local value in the Persistence store.
Finally calls the showWelcomeController method to let the user choose if he/she wants to login again or register a new user.
If you haven’t done it yet, launch the app with the rake command and see it in action in the simulator:
That’s it for now! I will post the second part of this tutorial soon: it will feature the completion of this ToDo app in order to get the list of user’s tasks from the backend, the creation of new tasks and their flagging as “completed”.