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

name

Project name, as a String. The default value is the name passed to motion create.

version

Project version, as a String. The default value is "1.0".

short_version

Project short version, as a String. This value must be unique for each version released to the App Store. The default value is "1".

identifier

Project identifier, as a String, in reverse DNS format. The default value is the concatenation of "com.yourcompany." and the name variable.

delegate_class

Name of the application delegate class, as a String. The default value is "AppDelegate" and the class is defined in app/main.rb.

files

Project files, as an Array. The default value is an array containing all .rb file in the app directory.

frameworks

The names of iOS or OS X frameworks to link against, as an Array. It should contain the names of public frameworks, typically present in /System/Library/Frameworks. The build system is capable of dealing with dependencies, for instance there is no need to mention "CoreFoundation" if you have "Foundation". The default value for iOS projects is [_UIKit_, _Foundation_, _CoreGraphics_], and for OS X projects, [_AppKit_, _Foundation_, _CoreGraphics_].

weak_frameworks

Similar to frameworks but only links if it is available for the deployment_target. This correlates to XCodes Optional setting and useful when a framework is not available for the projects deployment_target. Refer to iOS Frameworks to verify the required framework. The default value is [], an empty Array.

libs

Library paths to link against, as an Array. It should contain paths to public, system libraries, for example "/usr/lib/libz.dylib". The default value is [], an empty array.

build_dir

Path to the directory for build products, as a String. It must be relative to the project directory. The directory will be created by the build system if it does not exist yet. If it cannot be created, a temporary directory will be used instead. The default value is "build".

resources_dirs

Directories for resources files, as an Array of String. The default value is ["resources"].

specs_dir

Directories for specification files, as an Array of String. It must be relative to the project directory. The default value is "spec".

xcode_dir

Directory where Xcode is installed, as a String. The default value is determined by the returned value of '/usr/bin/xcode-select --print-path', or, if invalid, /Applications/Xcode.app/Contents/Developer. Giving a new value to this setting must be done first, before changing other settings.

sdk_version

Version number of the iOS or OS X SDK to use, as a String. The default value is the version number of the most recent supported SDK in xcode_dir. Example: "5.0".

deployment_target

Version number of the SDK to target, as a String. The default value is the value of sdk_version, but can be lowered to target older versions of iOS or OS X. Example: "4.3".

codesign_certificate

The name of the certificate to use for codesigning, as a String. The default value is the first iPhone or Mac Developer certificate found in keychain. Example: "iPhone Developer: Darth Vader (A3LKZY369Q)".

2.2. iOS Options

Variable Discussion

icons

List of names of resource files to use for icons, as an Array. For example, ["Icon.png", "Icon-72.png", "Icon@2x.png"]. The files must conform to the HIG guidelines. The default value is [], an empty array.

fonts

List of names of font files in the resources directory, as an Array. For example, ["Inconsolata.otf"]. The fonts will then be properly taken into account when generating the application. The default value is the list of all .ttf and .otf files in the resources directory. It is recommended to keep the default value.

prerendered_icon

Whether the image files in icons are already pre-rendered according to the HIG guidelines. If false, iOS will apply a reflective shine effect on the icons. The default value is false.

device_family

Family of devices to support. Possible values can be: iphone, ipad or [:iphone, :ipad] (for a universal application). The default value is :iphone.

interface_orientations

Supported interface orientations. Value must be an Array of one or more of the following symbols: :portrait, :landscape_left, :landscape_right, and :portrait_upside_down. The default value is [:portrait, :landscape_left, :landscape_right].

provisioning_profile

Path to the provisioning profile to use for deployment, as a String. The default value is the first .mobileprovision file found in '~/Library/MobileDevice/Provisioning Profiles'.

seed_id

The application provisioning identifier, as a String. It is a unique (within the App Store) 10 characters identifier generated by the provisioning portal. The default value is the first application identifier found in provisioning_profile.

2.3. OS X Options

Variable Discussion

icon

The name of the icon resource file to use as the application icon, as a String. For example, "Icon.png". The default value is '', an empty string.

copyright

The human-readable copyright that will be used in the application’s property file, as a String. The default value is "Copyright (c) CURRENT_DATE CURRENT_USER. All Right Reserved.".

embedded_frameworks

List of 3rd-party frameworks to embed in the application bundle, as an Array of String objects representing filesystem paths. For example, ["../MyFramework/framework"]. The default value is an empty array.

external_frameworks

List of frameworks to use that are outside of /System/Library/Frameworks and should not be embedded, as an Array of String objects representing filesystem paths. For example, ["/Library/Frameworks/iTunesLibrary.framework"]. The default value is an empty array.

codesign_for_development

Whether it codesigns the application for development build. If false, it skips codesigning. The default value is false.

codesign_for_release

Whether it codesigns the application for release build. If false, it skips codesigning. The default value is true.

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

:xcode

:xcodeproj

Name of the Xcode project file to use. Optional if there is one .xcodeproj file in the directory.

:target

Name of the target to build. If not provided, the default target will be used. Cannot be defined at the same time as :scheme.

:scheme

Name of the scheme to build. If not provided, the default scheme will be used. Cannot be defined at the same time as :target.

:configuration

Name of the configuration to build. If not provided, "Release" will be used.

:headers_dir

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 vendor_project, for example "Sources/Headers". This key is optional.

:products

An Array containing the names of products to use in the RubyMotion project, for example ["libfoo.a"]. This key can be used when the project builds more than one product and you want to filter what will be used by the app. If not provided, all .a libraries built will be used.

:bridgesupport_cflags

CFLAGS to use when generating BridgeSupport metadata, for example "-fobjc-arc" to turn on ARC. This key is optional.

:static

:products

An Array containing the names of static libraries to use. The default value is the list of all .a files in the vendor project directory.

:source_files

An Array of all source files and headers to compile. The default value is the list of all files in the vendor project directory matching "**/*.{c,m,cpp,cxx,mm,h}".

:cflags

CFLAGS to use for compilation, for example "-fobjc-arc" to turn on ARC. This key is optional.

:bridgesupport_cflags

CFLAGS to use specifically when generating BridgeSupport metadata, for example "-fobjc-arc" to turn on ARC. This key is optional and defaults to :cflags.

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:

  1. 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).

  2. 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.

  3. 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.

  4. 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.