Recently, RubyMotion has become an increasingly popular tool for developing for iOS. After a close acquaintance with him, it becomes clear why Ruby is a much more attractive language for this purpose than Objective-C.

Introduction
Is it Ruby or not?
RubyMotion is a ruby
language iOS development
toolkit . It consists of a
runtime environment that implements Ruby functionality within iOS. Although the scope of this ruby ​​code is different from
CRuby , the implementation is based on the specifications of Ruby 1.9.
Knowledge of Ruby does not guarantee that you can write iOS applications, but it definitely will not be superfluous. To work with RubyMotion, it’s much more important to be familiar with the Objective-C and Foundation Framework APIs. In essence, RubyMotion "codifies" Objective-C.
Project work
To work with RubyMotion, it is not necessary to use XCode, you can use your favorite editor. Together with RubyMotion comes a
console tool (
motion
) that helps you create and customize a folder with a project.
')
It also has a set of rake tasks that will help you build a project and run it in the iOS simulator. It can recognize resources, such as images or pfile, as well as connect and use .xib, .storyboard and .xcdatamodeld files.
Where to begin?
If you can’t wait to start work, you will have to
buy a license (
at the moment it costs $ 199, the website also shows the amount in rubles according to the curved exchange rate ), since This is a proprietary software. After that, there is no harm in going through the
Getting Started Guide on the RubyMotion site.
An
introductory screencast from the Motion Casts site will also be useful. And there is a
50-minute screencast from Pragmatic Studio, which will tell you how to create a simple application. This
tutorial is also good.
Simple application
We figured out what RubyMotion is and how it can be useful, so let's work on our first application.
Since I could not find any ready more or less complex example, I offer you my own crafts. Sources
are attached .
Our application
Let's pretend that we are doing the applique for the conference. For this, I will use the schedule
MagmaRails 2012 . The amount of information displayed is limited to the speakers and their speeches, divided by day.
Our interface
All resources on the RubyMotion developer site show how to build an interface using code, there are even some DSLs specially created for building user interfaces for iOS. But instead, we will use Xcode Storyboard, which will help us build a model for navigating the application.
Storyboards are tied to Xcode, and I previously promised that we will be able to develop applications without the need for it, but in this case we will use it simply because Storyboards are a very convenient visualization tool.
Preparing a project
The first step we will take is to create a new XCode project - it is assumed that you will use XCode 4.5.x for this - based on the Master-Detail Application, so give it a name, and also make sure that iPhone is selected in the Devices field. The checkboxes “Use StoryBoard” and “Use Core Data” are checked.

Now let's create a project in RubyMotion and call it “conference”:
$ motion create conference
Inside the structure of our RubyMotion project, there is a resources folder, into which you need to copy the
MainStoryboard.storyboard
file, which was created by XCode. Copy the file and then remove the link to it from XCode.

Since we still want to be able to edit this file in XCode, you need to drag it from our daddy to the XCode interface. We will be asked how exactly we want to do this. Make sure that the
Copy items into destination group's folder (if needed)
unchecked, and then click Finish. So we created a symlink to our file, and all changes inside Xcode will be automatically reflected in the project.

Now let's configure the Bundler:
$ bundle init
Edit the gemfile as follows:
source :rubygems gem 'xcodeproj', '~> 0.3.0' gem 'ib' gem 'rake'
Run the Bundler:
$ bundle
The
xmesproj and
ib gems will help connect the Ruby Outlets with the Xcode Storyboards, which we will use later.
Now we’ll correct Rakefile and add, after the line,
require "motion/project"
code that connects the Bundler to the project:
require 'rubygems' require 'ib' require 'bundler' Bundler.require
Find
app_delegate.rb
and open it in your favorite editor. Inside it, the
application
method is declared, which is the entry point of our application. Now we tell RubyMotion to download the
MainStoryboard.storyboard
:
def application(application, didFinishLaunchingWithOptions:launchOptions) @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds) @storyboard ||= UIStoryboard.storyboardWithName('MainStoryboard', bundle:NSBundle.mainBundle) @window.rootViewController = @storyboard.instantiateInitialViewController @window.rootViewController.wantsFullScreenLayout = true @window.makeKeyAndVisible true end
We initialize the screen frame, load the Storyboard, set the main Storyboard controller as our
rootViewController
and make our screen visible.
To test, run:
$ rake
This command will compile our project in the iOS application, load it into the simulator and launch it. If everything went well, you will see an empty grid. To close an application, type
quit
in the console.

Create a Storyboard
Going back to Xcode, let's open the
MainStoryboard.storyboard
file.

Select
Master View Controller
and double click on the title to change it to
MagmaRails
. Now select the
Table View
and change the value of the
Content
property from
Dynamic
to
Static
. The label should now have three rows of cells. You need to select the first one and make two copies of it. Change the title of each of them - double-click - to "Day One", "Day Two", "Day Three", "Speakers" and "Venue". Click on the transition connecting the Master View Controller and the Detail View Controller to remove it.

We need another View Controller to display conferences by day, so let's drag it from the Objects Library directly to the canvas. Drag the table view to this new controller, and the table-view cell in the table view.
Using identity inspector, rename the controller to
TalksViewController
. In the attribute inspector, change the identifier of our new cell to Talk, and from the identity inspector, change the class of the cell to TalkViewCell. Also, make sure that our cell style is custom and change its height to 115.
Add in the cell 3 labels and a picture that will display the information we need. Once done, drag the cursor while holding the
ctrl
key from the Day One cell on the Master View Controller to the new TalksViewController to create the transition. As the type of transition you need to select
push
, and as the name -
DayOne
. Do the same for cells with the second and third day.

Core Data models and application seed
Before we continue, let's create the
Core Data models as well as the basic information sid.
When we created the project in Xcode, the Use Core Data option was checked. This means that together with our project an
.xcdatamodeld
file was created. Find this file in Xcode and right click on it. Navigate to it using Show File in Finder, move it to the resources folder of our application and remove the link to the file in Xcode, then drag it back, but from our folder.
In Xcode, open Core Data Models and add two new entities, Talk and Presenter. For Talk, add the attributes as shown in the image and verify with the Data Model Inspector that the Class is called Talk. Repeat the same for Presenter.


Save the file. Now we are ready to create Models inside RubyMotion.
Let's connect two gems. The first,
motion-cocoapods , will allow us to manage dependencies through
CocoaPods , the dependency manager for Objective-C. Yes, this means that even if we write code in Ruby, we can still freely use Objective-C libraries.
The library we will use is called
MagicalRecord . It will allow us to work easily with Core Data.
The second gem is called
motion_support . It provides us with functionality similar to ActionSupport in Ruby on Rails (declensions and some kernel extensions), but with its own specifics. Add these gems to the Gemfile and run the Bundler.
gem 'motion-cocoapods' gem 'motion_support'
To make both hemes available, open your Rackfile and add two lines to it immediately below the mention of rubygems.
require 'motion-cocoapods' require 'motion_support/all'
We also need to tell RubyMotion that we want to use the Core Data framework and connect the MagicalRecord from cocoapods. For these purposes, we will force RubyMotion to load files into
app/lib
before the rest, so let's fix app.files.
Add to the end of the Motion :: Project :: App.setup block:
app.frameworks += %w(CoreData) app.files.unshift Dir.glob(File.join(app.project_dir, 'app/lib/**/*.rb')) app.pods do pod 'MagicalRecord' end
After these changes, you need to install motion-cocoapods:
$ pod setup $ rake UPDATE=1
These two lines will configure cocoapods and install the Objective-C libraries we specified in the Rakefile.
After all this, you can start using Core Data.
You need to create a class for each model that we described in the Core Data digram. Create a model directory inside the app, and in it the
Talk.rb
and
Presenter.rb
files:
class Talk < NSManagedObject end class Presenter < NSManagedObject end
In order for our models to behave like Core Data entities, you need to declare a couple of methods. It is important to note that we do not need to declare any properties for storing data and relationships in the model. These methods were declared for us during the configuration of the Core Data chart.
Our models will need methods to fill themselves with data from hashes, as well as a couple of finders. For this we need
MagicalRecord
.
Also, inside the
app_delegate.rb
file
app_delegate.rb
we will need a method for filling the database for the first time. This method will be called when the application starts. Seed data is stored in a plist file, which is transformed into a hash, and then used to populate our models and save them to the database.
def seedDatabase MagicalRecord.setupCoreDataStackWithStoreNamed('database.sqlite') if Talk.allTalks.size == 0
We work on controllers
Earlier we linked three MasterViewController and TalksViewController transitions, one for each conference day.
Since all 3 transitions point to the same controller, we need to use identifiers so that the TalksViewController understands which day is being discussed.
Create a
masterviewcontroller.rb
file inside the
app/controllers
and add the
prepareForSegue
method in it:
class MasterViewController < UITableViewController def prepareForSegue(segue, sender: sender) case segue.identifier when 'DayOne', 'DayTwo', 'DayThree' segue.destinationViewController.setFilter segue.identifier end end end
The
destinationViewController
property refers to the target controller, in our case
TalksViewController
, and it is assumed that we have a criterion by which we can filter the performances.
So let's create our
TalksViewController
. This controller will be the delegator of TableUIView, therefore we need to add two more methods:
class TalksViewController < UIViewController attr_accessor :filter attr_accessor :dataSource def tableView(tv, numberOfRowsInSection:section) self.dataSource.count end def tableView(tv, cellForRowAtIndexPath:indexPath) @reuseIdentifier ||= 'TalkCell' cell = tv.dequeueReusableCellWithIdentifier(@reuseIdentifier) || begin TalkCell.alloc.initWithStyle(UITableViewCellStyleDefault, reuseIdentifier:@reuseIdentifier) end talk = self.dataSource[indexPath.row]
In the
viewDidLoad
method, based on the value of the filter passed to
prepareSegue
, we select suitable reports from the database and save them in the
dataSource
property.
The
tableView(tv, numberOfRowsInSection:section)
method
tableView(tv, numberOfRowsInSection:section)
needs to know how many rows we want to display in the table, and we simply know this information from the
datasource
.
tableView(tv, cellForRowAtIndexPath:indexPath)
somewhat more confusing, this method is called every time a row is displayed on the screen, and we need to pass a cell object here with all the relevant data. If we have dozens or hundreds of records, this can lead to unwanted memory waste. Therefore, the framework stores a pool of previously instantiated cells, and instead of creating a new cell each time, it takes the old one from the pool and reuses it. Due to this, the number of objects in the memory is always small. It uses cells in accordance with the identifier, and if not available at the moment, it creates a new one.
In the case of our Storyboard, we created a custom cell for information about the report, and also added TalkCell as an identifier that has several labels and a picture. So let's create a new
TalkViewCell
class inside
/app/cells/talkviewcell.rb
:
class TalkViewCell < UITableViewCell end
The next step is to create IB Outlets that will allow us to connect our code with Storyboard labels and pictures, in which gem ib will help us:
class TalkViewCell < UITableViewCell extend IB outlet :talk, UILabel outlet :speaker, UILabel outlet :day, UILabel outlet :picture, UIImageView end
Once our outlets are in place, they need to be connected at Storyboard. If you have Xcode open, close it and run the following command (provided by ib):
$ rake ib:open
This will open a fake ib Xcode project, which you can immediately close. Open Storyboard in Xcode and select TalkViewCell, then click on the connections inspector. You should see all the outlets we announce, connected to each other.
Drag the outlet onto the canvas by pulling the circle to the right of its name and save the Storyboard.

Open the TalkViewCell class again and add the following method that will be used to set the contents of the cell:
def setupTalk(talk) self.talk.text = talk.title self.speaker.text = talk.presenter.name self.picture.image = UIImage.imageNamed(talk.presenter.picture) self.day.text = "Day #{talk.day}, #{talk.time}" end
Now go back to TalksViewController, and replace the comment
"# We will come back to this a bit later"
at the bottom of the
tableView(tv, cellForRowAtIndexPath:indexPath)
with the following code:
cell.setupTalk(talk) cell
Run the simulator:
rake
It should turn out as in the screenshot.

When you click on Day Two, you will receive a list of all performances for that day:

Finally
You are reading these lines, which means that you have managed to build an iPhone application. It uses:
- Bundler and gems created specifically for RubyMotion
- Blend of Cocoapods and Objective-C Libraries
- XCode charts Core Data and local sqlite database
- Xcode Interface Builder with Ruby Code
If you want to play with the code from this post, then welcome to the
repository .
RubyMotion makes iOS development a pleasure.
Additional Information
From translator
If you started to go through the tutorial and found any inaccuracies or errors, I will be grateful if you write me about this in PM.
RubyMotion is really gaining momentum, although many people with whom I spoke are skeptical about it. Yes, it is expensive, but damp. But if you use Ruby in your daily development, you should understand the speed with which projects are usually developed thanks to the community.
I bought myself a license, and in the near future I am going to sort out the question somewhat deeper. From myself I can add a number of interesting links:
Enjoy the development, make good applications and see you soon. Thanks for attention!