Ruby on Rails and RubyMotion Authentication Part One
A Complete iOS App with a Rails API backend
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.
I advise to read the nice getting started tutorial written by Clay Allsopp, one of the most active developers of the RubyMotion community.
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.
UPDATE May 15th: I decided to start using Bundler. For more information on why, check this useful guide.
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.
The WelcomeController
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.
Install Formotion
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.
RegisterContoller
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.
For more information on the Formotion DSL, check the official documentation.
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.
LoginController
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
TasksListController
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.
Conclusion
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”.
For any question, just like always, write a comment below or send me an email to luca.tironi@gmail.com.
I hope you enjoyed this tutorial and it was helpful for your projects. Have fun and see you soon!
Luca
^Back to top