RubyMotion Project Management Guide for iOS and OS X
In this guide we will explain how to create new RubyMotion iOS or OS X projects, then configure and maintain them.
1. Creation
To create a new RubyMotion project, pass the create
command to /usr/bin/motion. It will create a new project directory.
$ motion create Hello
$ cd Hello
$ ls
Rakefile app resources spec
1.1. Project Templates
By default, the motion create command will create a RubyMotion iOS project. The same command will accept the --template=
argument in order to create a different type of project.
RubyMotion currently ships with several templates. In this document, we will focus on the following two:
-
ios
: will create a RubyMotion iOS project. This is the template used by default. -
osx
: will create a RubyMotion OS X project.
For example, to create an OS X project:
$ motion create --template=osx Hello
$ cd Hello
Developers can add 3rd-party templates in the ~/Library/RubyMotion/template
directory. A RubyMotion project template is a directory that includes a files
sub-directory which will contains the files that will be created by motion create.
You can refer to the builtin templates in /Library/RubyMotion/lib/motion/project/template
for documentation.
1.2. Project Anatomy
The following table illustrates the anatomy of a project directory.
File/Directory | Purpose |
---|---|
Rakefile |
This file contains the configuration of the project, as well as a default set of tasks. It can be edited to change the configuration or add new tasks. |
.gitignore |
This file contains file patterns that should be ignored by the source control management software (for instance, build files). This file is used by Git, but it can be ported to other SCMs. |
app/ |
This directory contains the Ruby code of the project. In a new project, a main.rb file is created automatically. |
resources/ |
This directory contains the resources files of the project, such as images or sounds. In a new project, this directory is empty. |
spec/ |
This directory contains the specification files of the application. In a new project, a default specification file is created automatically. |
RubyMotion projects are based on Rake. Essential rake tasks will be covered in the following sections. To see the full list of available tasks:
$ rake -T
Tip
|
Rake is the de-facto build system framework for Ruby. It is similar to make and ships by default in OS X. |
2. Configuration
The rake config task will dump the project configuration.
$ rake config
Each configuration variable has a sensible default value that can be manually overriden in the Rakefile file.
2.1. Common Options
Variable | Discussion |
---|---|
|
Project name, as a |
|
Project version, as a |
|
Project short version, as a |
|
Project identifier, as a |
|
Name of the application delegate class, as a |
|
Project files, as an |
|
The names of iOS or OS X frameworks to link against, as an |
|
Similar to |
|
Library paths to link against, as an |
|
Path to the directory for build products, as a |
|
Directories for resources files, as an |
|
Directories for specification files, as an |
|
Directory where Xcode is installed, as a |
|
Version number of the iOS or OS X SDK to use, as a |
|
Version number of the SDK to target, as a |
|
The name of the certificate to use for codesigning, as a |
2.2. iOS Options
Variable | Discussion |
---|---|
|
List of names of resource files to use for icons, as an |
|
List of names of font files in the resources directory, as an |
|
Whether the image files in |
|
Family of devices to support. Possible values can be: |
|
Supported interface orientations. Value must be an |
|
Path to the provisioning profile to use for deployment, as a |
|
The application provisioning identifier, as a |
2.3. OS X Options
Variable | Discussion |
---|---|
|
The name of the icon resource file to use as the application icon, as a |
|
The human-readable copyright that will be used in the application’s property file, as a |
|
List of 3rd-party frameworks to embed in the application bundle, as an |
|
List of frameworks to use that are outside of |
|
Whether it codesigns the application for development build. If |
|
Whether it codesigns the application for release build. If |
2.4. Providing Custom Values
Custom values for the configuration settings can be added by tweaking the Motion::App.setup
block in the Rakefile
file.
As an example, let’s take the configuration block of a fictional video player application for the iPad. The device family setting has to change from its default value, iPhone, to iPad. Also, the application makes use of an additional framework, AVFoundation, for audio-video functionality.
Motion::Project::App.setup do |app|
app.name = 'Awesome Video Player'
app.device_family = :ipad
app.frameworks += ['AVFoundation']
end
2.5. Files Dependencies
By default, RubyMotion will compile files in the regular sorting order of the filesystem. When a RubyMotion application starts, the main scope of each file will then be executed in that specific order.
Sometimes, you will want to customize the order, if for instance one file makes use of a constant defined in another.
$ cat app/foo.rb
class Foo
end
$ cat app/bar.rb
class Bar < Foo
end
In the example above, using the default order, bar.rb will be compiled before foo.rb resulting in a constant lookup error, because the Foo
constant has not been defined yet when we execute the code in bar.rb.
To fix that problem, the files_dependencies
method can be used in the Rakefile. This method accepts a Hash
which should be a set of files dependencies.
Motion::Project::App.setup do |app|
# ...
app.files_dependencies 'app/bar.rb' => 'app/foo.rb'
end
After this change, the build system will compile foo.rb before bar.rb.
2.6. Vendoring 3rd-Party Libraries
The iOS and OS X SDK has a significant amount of functionality built-in that you can use in your RubyMotion project. However, sometimes you will have to use a 3rd-party library that provides a feature missing from the system.
To vendor a 3rd-party library in a RubyMotion project, the source code must be available somewhere on the filesystem. It is recommended to keep the 3rd-party libraries required by the project in the same place, for instance under a vendor directory.
The vendor_project
method can be called from the Rakefile. Its first argument must be the path to the 3rd-party library, for example "vendor/OAuth2Client"
. Its second argument must be a symbol representing the project type, like :xcode
. Other arguments can be provided as a list of key/value objects.
Here is a table summarizing project types and key/value objects.
Project Type | Key | Discussion |
---|---|---|
|
|
Name of the Xcode project file to use. Optional if there is one .xcodeproj file in the directory. |
|
Name of the target to build. If not provided, the default target will be used. Cannot be defined at the same time as |
|
|
Name of the scheme to build. If not provided, the default scheme will be used. Cannot be defined at the same time as |
|
|
Name of the configuration to build. If not provided, |
|
|
Path to the directory that contains public headers files, declaring APIs that will be used by the RubyMotion project. The path should be relative to the path provided to |
|
|
An |
|
|
|
|
|
|
An |
|
An |
|
|
|
|
|
|
Continuing our example from above, assuming our video player project wants to make use of the OAuth2Client 3rd-party library, a vendor directory would be created and the OAuth2Client source code would be added there.
$ cd AwesomeVideoPlayer
$ ls vendor
OAuth2Client
Then, the Rakefile can be modified as such.
Motion::Project::App.setup do |app|
# ...
app.vendor_project('vendor/OAuth2Client', :xcode,
:target => 'OAuth2Client',
:headers_dir => 'Sources/OAuth2Client')
app.frameworks << 'Security' # OAuth2Client depends on Security.framework
end
2.7. Embedded Frameworks
Note
|
OS X only. |
OS X applications can embed 3rd-party frameworks inside their bundle directory. This can be used as an alternative to vendoring static libraries, as described above.
To embed a 3rd-party framework in your app, you can use the app.embedded_frameworks
variable in the Rakefile.
Motion::Project::App.setup do |app|
# ...
app.embedded_frameworks << '../MyFramework.framework'
end
Embedded frameworks are copied inside the Contents/Frameworks directory of the application bundle. The application binary executable will also be reconfigured to have a relative link path to the framework.
2.8. Entitlements
Note
|
iOS only. |
Entitlements confer specific capabilities or security permissions to an application. You may be required by Apple to request an entitlement when trying to access a specific feature of the system.
An application running on an iOS device that does not have the proper entitlement for a functionality will fail at runtime when trying to use such functionality. It will also not be accepted into the App Store.
Entitlements are used during the code-signing part of the build process.
The entitlements
method of the Rakefile configuration object returns an empty Hash
object by default, that you can modify to set appropriate keys and values.
For instance, our video player might require access to the keychain to store the user credentials. According to the documentation, the keychain-access-groups
entitlement must be requested, passing a combination of the application provisioning identifier and the application identifier, respectively exposed as seed_id
and identifier
in RubyMotion.
Motion::Project::App.setup do |app|
# ...
app.entitlements['keychain-access-groups'] = [
app.seed_id + '.' + app.identifier
]
end
2.9. Advanced Info.plist Settings
An iOS app has its configuration defined in the Info.plist file, which is located inside the application bundle. This file contains a set of keys and values. It is fully documented in the Info.plist reference guide.
In a RubyMotion project, the Info.plist file is derived from the configuration object exposed in the Rakefile file. For example, the CFBundleName
variable in Info.plist is derived from the name
variable in the Rakefile. Most of the time, the configuration object will let you control the necessary settings of his project.
However, it might happen that you will want to change an advanced setting of a project. The Rakefile interface does not cover all the possible settings, but it exposes the internal Info.plist data structure that one can modify if needed.
As an example, our video player might need to register a custom URI scheme, so that it can open custom URLs from the web browser, for instance, x-videoplayer://play
. The Rakefile configuration object does not provide support for this.
The reference suggests that the CFBundleURLTypes
key should be used. The key can be manually set by using the info_plist
method, which returns a mutable Hash
object.
Motion::Project::App.setup do |app|
# ...
app.info_plist['CFBundleURLTypes'] = [
{ 'CFBundleURLName' => 'com.mycompany.x-videoplayer',
'CFBundleURLSchemes' => ['x-videoplayer'] }
]
end
3. Build
The rake build task builds the project into the temporary build directory.
In an iOS project, two different versions of the project will be built, one to run in the iOS simulator (on the Mac itself) and one to run on the iOS device. In an OS X project, only one version will be built.
The following steps are performed during the build process:
-
It compiles each Ruby source code file into optimized machine code, translating the Ruby syntax tree into an intermediate representation language (using LLVM), then assembly. For iOS, the compiler will generate code for either the Intel 32-bit (
i386
) or ARM (armv6
,armv7
) instruction sets and ABIs depending on the target. For OS X, the compiler will target both Intel 32-bit (i386
) and 64-bit (x86_64
). -
It links the machine code with the RubyMotion runtime statically to form an executable. The linker also includes metadata for the C APIs that the project uses, as well as 3rd-party libraries vendored from the configuration.
-
It creates an .app bundle and copies the executable there. The Info.plist file is generated based on the project configuration. Each resource file in the resources directory is copied in the bundle. Old resource files, that have since been deleted from the project, will not be present in the application bundle.
-
It codesigns the bundle based on the certificate, the provisioning profile and the entitlements specified in the project configuration.
Normally the user does not need to explicitly build the project, as the build task is a dependency of the other tasks.
3.1. Xcode Resource Files
The build system will detect the following Xcode resource files in the resources directory and properly compile them and copy the result into the generated .app bundle.
Source Extension | Compiled Extension | Description |
---|---|---|
.xib |
.nib |
Interface Builder files. |
.storyboard |
.storyboardc |
Storyboard files. |
.xcdatamodeld |
.momd |
CoreData model files. |
The compiled files will be removed by 'rake clean'.
3.2. Parallel Compilation
The compilation of Ruby source files into machine code takes a non-negligible amount of time.
If the machine used to build the project runs a multicore processor, which is very likely these days, the build system will try to spread the compilation tasks in parallel. This can be very useful when building a project that contains a significant number of files.
The build system uses the value of the machdep.cpu.thread_count
sysctl kernel variable to determine know how many compilation tasks can be executed in parallel. For example, a MacBook Pro running an Intel Core i7 processor has 4 available cores, each one being able to run 2 concurrent threads, and the build system will therefore compile 8 files at a time.
It is possible to override the number of concurrent jobs the build system should use by setting the jobs
environment variable. It can be set to a lower number if for instance the machine is performing another CPU intensive task that should not be interrupted by the build system.
$ rake jobs=1 # Force compilation tasks to be sequential.
3.3. Cleaning
The rake clean task empties the build directory and cleans the build directories of the vendored projects.
$ rake clean
4. Running
The default rake
task will build the project and run it locally on your Mac.
$ rake
In an iOS project, the application will run in the iOS simulator. The default rake
task is a shortcut to rake simulator.
In an OS X project, the application will run natively. The default rake
task is a shortcut to rake run.
4.1. Passing Arguments
During development, you may have to pass command-line arguments when launching the application.
The rake
task honors the args
environment variable, which can be used to provide arguments. As an example, to enable CoreData SQL debug:
$ rake args="-com.apple.CoreData.SQLDebug 1"
4.2. Interactive Console
An interactive shell, similar to Ruby’s own irb
command, is available when you run your app locally.
You can evaluate any expression in the shell. The shell does not block the app from running, but expressions typed into it will be sent to the app which will execute them in the main thread.
Note
|
The REPL (read-eval-print-loop) support is loaded on demand in the app, via a shared library. By default, an app does not contain any REPL-related code, for example, iOS projects built for the device. |
You can type help
to get more information about the built-in expressions.
The interactive shell also features a way to select views in the app. Hold the command
key while moving your mouse over the app window and you should see red borders around views you are selecting as well as the object in the shell. Once you click, the shell will open a new session whose context (the self
object) is the view you selected.
5. Simulators
Note
|
iOS-only. |
A universal application targets both the iPhone and iPad device families (see the device_family
project configuration setting for more details). Additionally, there are different iPhone screen sizes you need to support.
In this case, it can be convenient to specify which device you want to simulate. Xcode lets you manage and create different simulators, assigning a custom name, device type and iOS SDK version. You can access this inside Xcode in "Window > Devices".
The device_name
environment variable lets you configure in which simulator the app will run.
$ rake simulator device_name="iPad 2"
Note
|
Xcode may create multiple simulators with the same name, running different versions of iOS. In order to select which simulator will run via the device_name variable, you have to rename the simulator in Xcode (or via the xcrun simctl command line utility) to a unique name (e.g. "iPhone 5 7.0"). |
5.1. Cleaning the Sandbox
Note
|
iOS-only. |
Each application lives in its own directory inside the iOS simulator sandbox. This directory contains the application bundle, but also the Documents and Library folders, which store its filesystem state. When running an application through the simulator, the sandbox will be created if it doesn’t exist, otherwise, the application will be copied into the existing sandbox.
Sometimes, you may want to clean the application sandbox before running the simulator, in order to start from a fresh state. For instance, if resource files are removed from the project, or if the application has state data that has to be cleaned up.
To perform that, the clean
environment variable can be set to any value, which will trigger the removal of the application sandbox before running the simulator.
$ rake simulator clean=1
6. Archiving
RubyMotion projects can be archived in order to be distributed and submitted to the App Store.
6.1. Development vs Release
A RubyMotion project can be built for development or release. A project is built for development when you run it in the simulator, or push it to your development device. A project is built for release when you’re generating an archive for an App Store submission.
Currently, the main difference between a RubyMotion app built for release and one built for development is that all symbols are stripped from the main executable. This process removes about 1MB of data from the app bundle, but at the same time makes debugging more difficult, which is why it’s not applied in development.
For OS X projects, an app built for release will include full (both 32/64-bit) Intel architecture support. Development builds only target the local Intel architecture, to make builds faster.
Sometimes, you want to apply different settings in the Rakefile if the project builds for release. This can be done by using the development
and release
methods on the configuration object. These methods will yield the given block for the specified build mode.
Motion::Project::App.setup do |app|
# ...
app.development do
# This entitlement is required during development but must not be used for release.
app.entitlements['get-task-allow'] = true
end
end
6.2. Install on Device
Note
|
iOS-only. |
The rake device task builds and uploads a development archive of the application to an iOS device.
There must be one iOS device connected via USB to the Mac. The deployment task will attempt to upload the application to the first discovered iOS device on the USB channel.
The process will fail in the following cases:
-
No iOS device was found on USB.
-
The project builds on a version of iOS greater than the version of iOS running on the device.
-
The project doesn’t use the appropriate certificate and provisioning profile linked to the device.
-
There is a USB connection issue when talking to the device.
Otherwise, the process returns successfully and the application is then available on the device springboard.
6.3. Distribution
The rake archive task can be used to generate archives for both development and release modes. An archive is suitable for ad-hoc distribution and can also be used for a submission to the App Store.
On iOS projects, archive files have the .ipa file extension. On OS X projects, they have the .pkg extension.
For creating an archive for the App Store, use the rake archive:distribution task.
The task requires a valid certificate and provisioning profile in order to codesign the app bundle.
6.4. Manifest File
iOS projects can have a manifest.plist file generated during rake archive. This file can be used to implement over-the-air ad-hoc distribution
To enable the generation of this file, the app.manifest_assets
variable, which defaults to an empty Array
, has to include at least one Hash
object describing an asset. Each Hash
has to provide values for the kind
and url
keys. Values for the kind
key can be "software-package"
, "display-image"
and "full-size-image"
.
The following snippet describes 2 manifest assets, a pointer to the application archive and a pointer to an icon image describing the application.
Motion::Project::App.setup do |app|
# ...
app.manifest_assets << { :kind => 'software-package',
:url => 'http://mycompany.com/files/myapp.ipa' }
app.manifest_assets << { :kind => 'display-image',
:url => 'http://mycompany.com/files/myapp.jpg' }
end
At the time of this writing, Apple doesn’t provide documentation regarding the format of the properly list file, so you will have to search online to know more.