mirror of
https://github.com/zhigang1992/RubyMotion.git
synced 2026-01-13 09:39:37 +08:00
move to asciidoc
This commit is contained in:
@@ -1,127 +0,0 @@
|
||||
h1. Welcome!
|
||||
|
||||
Thanks for purchasing RubyMotion. This guide will help you getting started with the product.
|
||||
|
||||
h2. Overview
|
||||
|
||||
"RubyMotion":http://www.rubymotion.com is a toolchain that permits the development of iOS applications using the Ruby programming language.
|
||||
|
||||
"iOS":http://www.apple.com/ios is Apple's mobile operating system, powering a variety of devices such as iPhone, iPod touch and iPad. Developers can write applications for iOS and submit them to the App Store, Apple's application distribution system.
|
||||
|
||||
Conceptually, RubyMotion is a combination of three components:
|
||||
|
||||
* *Runtime*: a brand-new implementation of the Ruby language, tightly integrated with the native iOS runtime and optimized for embedded iOS devices constrains.
|
||||
* *Tool*: a command-line interface to create, manage and interactively develop RubyMotion projects. It also includes a static compiler that compiles Ruby into optimized machine code, and a fully-functional debugger.
|
||||
* *Library*: a set of high-level Ruby libraries sitting on top of the iOS SDK that greatly facilitates the development of applications. Each library covers a commonly used pattern, such as UI elements building, XML/JSON parsing, data persistence, and more.
|
||||
|
||||
RubyMotion installs itself into +/Library/Motion+. A symbolic link to the command-line interface is created as +/usr/bin/motion+.
|
||||
|
||||
h2. License Activation
|
||||
|
||||
You should have received an email with your purchase that contains a 40 hexadecimal characters license key. The key can be used to activate RubyMotion. Product activation is mandatory in order to receive software updates or file support tickets.
|
||||
|
||||
<pre>
|
||||
$ sudo motion activate <license-key...>
|
||||
</pre>
|
||||
|
||||
h2. Software Updates
|
||||
|
||||
Software updates can be applied via the command-line.
|
||||
|
||||
<pre>
|
||||
$ sudo motion update
|
||||
</pre>
|
||||
|
||||
This command will grab the latest version of RubyMotion from the network and install it. You must be connected to the Internet to perform that command.
|
||||
|
||||
You can always see the version number of the version of RubyMotion installed on the computer.
|
||||
|
||||
<pre>
|
||||
$ motion -v
|
||||
1.0
|
||||
</pre>
|
||||
|
||||
Once a day, the RubyMotion build system pings the software update server in order to see if a new version of RubyMotion is available to install, and accordingly prints a message on your terminal, suggesting you to upgrade.
|
||||
|
||||
h2. Support
|
||||
|
||||
If you are experiencing an issue, would like to request a feature, or simply have a question, you can file out a support ticket from the command-line too.
|
||||
|
||||
<pre>
|
||||
$ motion support
|
||||
</pre>
|
||||
|
||||
h2. Hello World
|
||||
|
||||
We are now ready to write our first RubyMotion program: Hello World.
|
||||
|
||||
Open your terminal and go to a place where you would like this first project to be created, then type the following command.
|
||||
|
||||
<pre>
|
||||
$ motion create Hello
|
||||
</pre>
|
||||
|
||||
This command will create a RubyMotion project in a new directory, called Hello. If this directory already exists or cannot be created, the command will fail.
|
||||
|
||||
Let's have a look inside.
|
||||
|
||||
<pre>
|
||||
$ cd Hello
|
||||
$ ls
|
||||
Rakefile app resources spec
|
||||
</pre>
|
||||
|
||||
A RubyMotion project is +Rakefile+-based. +rake+ is the de-facto Ruby build program. It is similar to +make+ and it ships with Mac OS X by default.
|
||||
|
||||
The +app+ directory contains the application code. The +resources+ directory will eventually contain the resource files of your project, such as icon, image or sound files. The +spec+ directory contains specification/test files.
|
||||
|
||||
Let's run the default task.
|
||||
|
||||
<pre>
|
||||
$ rake
|
||||
</pre>
|
||||
|
||||
This should build our project then start the simulator, and you should see... an empty, black window. It's actually normal, we haven't written any code yet!
|
||||
|
||||
If you look inside the +app+ directory you will see an +app_delegate.rb+ file, which is created by default. This file implements the +AppDelegate+ class, which is responsible to control your application.
|
||||
|
||||
<pre>
|
||||
class AppDelegate
|
||||
def application(application, didFinishLaunchingWithOptions:launchOptions)
|
||||
true
|
||||
end
|
||||
end
|
||||
</pre>
|
||||
|
||||
Open the +app/app_delegate.rb+ file with your favorite editor. We will change the code to do something more interesting, such as triggering an alert.
|
||||
|
||||
<pre>
|
||||
class AppDelegate
|
||||
def application(application, didFinishLaunchingWithOptions:launchOptions)
|
||||
alert = UIAlertView.new
|
||||
alert.message = "Hello World!"
|
||||
alert.show
|
||||
true
|
||||
end
|
||||
end
|
||||
</pre>
|
||||
|
||||
If you run the +rake+ command again from the terminal, you should be able to see the alert in the simulator.
|
||||
|
||||
Now, let's try this outside the simulator. Make sure you have an iOS device properly configured for development connected over USB, and type the following.
|
||||
|
||||
<pre>
|
||||
$ rake device
|
||||
</pre>
|
||||
|
||||
This should install the Hello app on your device. You can now pick it up and run the app, and you should be able to see the alert message.
|
||||
|
||||
Congratulations, you successfully created your first RubyMotion program. That wasn't too hard, wasn't it?
|
||||
|
||||
h2. And Now?
|
||||
|
||||
The "/Library/Motion/sample":file:///Library/Motion/sample directory contains sample code. Each of these is a RubyMotion project as described above, you can type +rake+ from their directory to build and run them.
|
||||
|
||||
The "/Library/Motion/doc":file:///Library/Motion/doc directory contains guides about the platform. You will find there essential information about the runtime and the project management interface.
|
||||
|
||||
Check out the "Developer Center":http://www.rubymotion.com/devcenter for more resources. You will find there tutorials and screencasts.
|
||||
126
doc/Getting Started.txt
Normal file
126
doc/Getting Started.txt
Normal file
@@ -0,0 +1,126 @@
|
||||
Welcome to RubyMotion
|
||||
=====================
|
||||
Laurent Sansonetti <lrz@hipbyte.com>
|
||||
|
||||
|
||||
Thank you for purchasing RubyMotion. This guide will help you getting started with the product.
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
http://www.rubymotion.com[RubyMotion] is a toolchain that permits the development of iOS applications using the Ruby programming language.
|
||||
|
||||
http://www.apple.com/ios[iOS] is Apple's mobile operating system, powering a variety of devices such as iPhone, iPod touch and iPad. Developers can write applications for iOS and submit them to the App Store, Apple's application distribution system.
|
||||
|
||||
Conceptually, RubyMotion is a combination of two major components:
|
||||
|
||||
* *Runtime*: a brand-new implementation of the Ruby language, tightly integrated with the native iOS runtime and optimized for embedded iOS devices constrains.
|
||||
* *Project Management*: a command-line interface to create, manage and interactively develop RubyMotion projects. It also includes a static compiler that compiles Ruby into optimized machine code and an interactive console where you can evaluate expressions on the fly and change the way your app behaves in real-time.
|
||||
|
||||
RubyMotion installs itself into '/Library/Motion'. A symbolic link to the command-line interface is created as '/usr/bin/motion'.
|
||||
|
||||
Software Updates
|
||||
----------------
|
||||
|
||||
Software updates can be applied via the command-line.
|
||||
|
||||
The following command will grab the latest version of RubyMotion from the network and install it. You must be connected to the Internet to perform that command.
|
||||
|
||||
----
|
||||
$ sudo motion update
|
||||
----
|
||||
|
||||
You can always see the version number of the version of RubyMotion installed on the computer.
|
||||
|
||||
----
|
||||
$ motion -v
|
||||
1.0
|
||||
----
|
||||
|
||||
Once a day, the RubyMotion build system pings the software update server in order to see if a new version of RubyMotion is available to install, and accordingly prints a message on your terminal, suggesting you to upgrade.
|
||||
|
||||
Support
|
||||
-------
|
||||
|
||||
If you are experiencing an issue, would like to request a feature, or simply have a question, you can file out a support ticket from the command-line.
|
||||
|
||||
----
|
||||
$ motion support
|
||||
----
|
||||
|
||||
This will open a new window in your browser where you can fill up a support ticket. Your license key and some useful information regarding your environment will be added automatically.
|
||||
|
||||
Hello World
|
||||
-----------
|
||||
|
||||
We are now ready to write our first RubyMotion program: Hello World.
|
||||
|
||||
Open your terminal and go to a place where you would like this first project to be created, then type the following command.
|
||||
|
||||
----
|
||||
$ motion create Hello
|
||||
----
|
||||
|
||||
This command will create a RubyMotion project in a new directory, called Hello. If this directory already exists or cannot be created, the command will fail.
|
||||
|
||||
Let's have a look inside.
|
||||
|
||||
----
|
||||
$ cd Hello
|
||||
$ ls
|
||||
Rakefile app resources spec
|
||||
----
|
||||
|
||||
A RubyMotion project is +Rakefile+-based. +rake+ is the de-facto Ruby build program. It is similar to +make+ and it ships with Mac OS X by default.
|
||||
|
||||
The 'app' directory contains the application code. The 'resources' directory will eventually contain the resource files of your project, such as icon, image or sound files. The 'spec' directory contains specification/test files.
|
||||
|
||||
Let's run the default task.
|
||||
|
||||
----
|
||||
$ rake
|
||||
----
|
||||
|
||||
This should build our project then start the simulator, and you should see... an empty, black window. It's actually normal, we haven't written any code yet!
|
||||
|
||||
If you look inside the 'app' directory you will see an 'app_delegate.rb' file, which is created by default. This file implements the +AppDelegate+ class, which is responsible to control your application object.
|
||||
|
||||
----
|
||||
class AppDelegate
|
||||
def application(application, didFinishLaunchingWithOptions:launchOptions)
|
||||
true
|
||||
end
|
||||
end
|
||||
----
|
||||
|
||||
Open the 'app/app_delegate.rb' file with your favorite editor. We will change the code to do something more interesting, such as triggering an alert.
|
||||
|
||||
----
|
||||
class AppDelegate
|
||||
def application(application, didFinishLaunchingWithOptions:launchOptions)
|
||||
alert = UIAlertView.new
|
||||
alert.message = "Hello World!"
|
||||
alert.show
|
||||
true
|
||||
end
|
||||
end
|
||||
----
|
||||
|
||||
If you run the +rake+ command again from the terminal, you should be able to see the alert in the simulator.
|
||||
|
||||
Now, let's try this outside the simulator. Make sure you have an iOS device properly configured for development connected over USB, and type the following.
|
||||
|
||||
----
|
||||
$ rake device
|
||||
----
|
||||
|
||||
This should install the Hello app on your device. You can now pick it up and run the app, and you should be able to see the alert message.
|
||||
|
||||
Congratulations, you successfully created your first RubyMotion program. That wasn't too hard, wasn't it?
|
||||
|
||||
And Now?
|
||||
--------
|
||||
|
||||
You will find sample code in the '/Library/Motion/sample' folder. Each of the sub-folders is a RubyMotion project as introduced above. You can type +rake+ from their directory to build and run them and check their source code by reading the files in the 'app' directory.
|
||||
|
||||
Make sure to check the http://www.rubymotion.com/developer-center[Developer Center] for more resources, such as guides and tutorials.
|
||||
@@ -1,130 +1,151 @@
|
||||
h1. Project Management
|
||||
RubyMotion Project Management Guide
|
||||
===================================
|
||||
Laurent Sansonetti <lrz@hipbyte.com>
|
||||
|
||||
This guide covers the way RubyMotion projects work, and how they are maintained.
|
||||
In this guide we will explain how to create new RubyMotion projects, then configure and maintain them.
|
||||
|
||||
h2. Creation
|
||||
Creation
|
||||
--------
|
||||
|
||||
RubyMotion projects are created by passing the +create+ command to +/usr/bin/motion+. RubyMotion projects are directories.
|
||||
To create a new RubyMotion project, pass the +create+ command to '/usr/bin/motion'. It will create a new project directory.
|
||||
|
||||
<pre>
|
||||
----
|
||||
$ motion create Hello
|
||||
$ cd Hello
|
||||
$ ls
|
||||
Rakefile app resources spec
|
||||
</pre>
|
||||
----
|
||||
|
||||
The following table explains the anatomy of a project directory.
|
||||
Project Anatomy
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
|_<. 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. |
|
||||
| +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. |
|
||||
The following table illustrates the anatomy of a project directory.
|
||||
|
||||
RubyMotion projects are managed by the +rake+ tool. Essential tasks will be covered in the following sections. To see the full list of available tasks:
|
||||
[options="header,autowidth"]
|
||||
|=========================
|
||||
| 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.
|
||||
|=========================
|
||||
|
||||
<pre>
|
||||
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
|
||||
</pre>
|
||||
----
|
||||
|
||||
h2. Configuration
|
||||
NOTE: Rake is the de-facto build system framework for Ruby. It is similar to make and ships by default in OSX.
|
||||
|
||||
The +rake config+ task will dump the project configuration. Each configuration variable has a sensible default value that can be manually overriden in the +Rakefile+ file.
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
|_<. 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'+. |
|
||||
|+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 the result of the following expression: +Dir.glob('./app/**/*.rb')+ (every +.rb+ file in the +app+ directory). |
|
||||
|+frameworks+ | The names of iOS frameworks to link against, as an +Array+. It should contain the names of public iOS 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 is +['UIKit', 'Foundation', 'CoreGraphics']+. |
|
||||
| +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_dir+ | Directory for resources files, as a +String+. It must be relative to the project directory. The default value is +'resources'+. |
|
||||
|+specs_dir+ | Directory for specification files, as a +String+. It must be relative to the project directory. The default value is +'spec'+. |
|
||||
|+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":http://developer.apple.com/library/ios/#documentation/userexperience/conceptual/mobilehig/IconsImages/IconsImages.html. 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]+. |
|
||||
|+xcode_dir+ | Directory where Xcode is installed, as a +String+. The default value is determined by the returned value of +'/usr/bin/xcode-select -printPath'+, 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 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. Example: +'4.3'+. |
|
||||
|+codesign_certificate+ | The name of the certificate to use for codesigning, as a +String+. The default value is the first iPhone Developer certificate found in keychain. Example: +'iPhone Developer: Darth Vader (A3LKZY369Q)'+.|
|
||||
|+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+. |
|
||||
|+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+. |
|
||||
The +rake config+ task will dump the project configuration. Each configuration variable has a sensible default value that can be manually overriden in the 'Rakefile' file.
|
||||
|
||||
[options="header,autowidth"]
|
||||
|====
|
||||
| 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"+.
|
||||
|+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 the result of the following expression: +Dir.glob('./app/**/*.rb')+ (every '.rb' file in the 'app' directory).
|
||||
|+frameworks+ | The names of iOS frameworks to link against, as an +Array+. It should contain the names of public iOS 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 is +['UIKit', 'Foundation', 'CoreGraphics']+.
|
||||
| +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_dir+ | Directory for resources files, as a +String+. It must be relative to the project directory. The default value is +"resources"+.
|
||||
|+specs_dir+ | Directory for specification files, as a +String+. It must be relative to the project directory. The default value is +"spec"+.
|
||||
|+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 http://developer.apple.com/library/ios/#documentation/userexperience/conceptual/mobilehig/IconsImages/IconsImages.html[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]+.
|
||||
|+xcode_dir+ | Directory where Xcode is installed, as a +String+. The default value is determined by the returned value of '/usr/bin/xcode-select -printPath', 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 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. Example: +"4.3"+.
|
||||
|+codesign_certificate+ | The name of the certificate to use for codesigning, as a +String+. The default value is the first iPhone Developer certificate found in keychain. Example: +"iPhone Developer: Darth Vader (A3LKZY369Q)"+.
|
||||
|+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'.
|
||||
|+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+.
|
||||
|====
|
||||
|
||||
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.
|
||||
|
||||
<pre>
|
||||
----
|
||||
Motion::Project::App.setup do |app|
|
||||
app.name = 'Awesome Video Player'
|
||||
app.device_family = :ipad
|
||||
app.frameworks += ['AVFoundation']
|
||||
end
|
||||
</pre>
|
||||
----
|
||||
|
||||
h3. Files Dependencies
|
||||
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, the developer will want to customize the order, if for instance one file makes use of a constant defined in another.
|
||||
|
||||
<pre>
|
||||
----
|
||||
$ cat app/foo.rb
|
||||
class Foo
|
||||
end
|
||||
$ cat app/bar.rb
|
||||
class Bar < Foo
|
||||
end
|
||||
</pre>
|
||||
----
|
||||
|
||||
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+.
|
||||
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.
|
||||
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.
|
||||
|
||||
<pre>
|
||||
----
|
||||
Motion::Project::App.setup do |app|
|
||||
# ...
|
||||
app.files_dependencies 'app/bar.rb' => 'app/foo.rb'
|
||||
end
|
||||
</pre>
|
||||
----
|
||||
|
||||
After this change, the build system will compile +foo.rb+ before +bar.rb+.
|
||||
After this change, the build system will compile 'foo.rb' before 'bar.rb'.
|
||||
|
||||
h3. Using 3rd-Party Libraries
|
||||
Using 3rd-Party Libraries
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The iOS SDK has a significant amount of functionality built-in that one can use from a RubyMotion project, however, sometimes a developer will need to use a 3rd-party library that provides a missing feature of iOS.
|
||||
|
||||
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 at the same place, for instance under a +vendor+ directory.
|
||||
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 at 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.
|
||||
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 |
|
||||
|/6. +: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 used at the same time than +:scheme+. |
|
||||
|| +:scheme+ | Name of the scheme to build. If not provided, the default scheme will be used. Cannot be used at the same time than +: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 the developer wants to filter what will be used by the app. If not provided, all +.a+ libraries built will be used. |
|
||||
|/2. +: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. |
|
||||
|| +: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 +"include"+. The default value is the vendor project directory.|
|
||||
[cols=".^,.^,.^",options="header,autowidth"]
|
||||
|====
|
||||
| Project Type | Key | Discussion
|
||||
.6+|+: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 used at the same time than +:scheme+.
|
||||
| +:scheme+ | Name of the scheme to build. If not provided, the default scheme will be used. Cannot be used at the same time than +: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 the developer wants to filter what will be used by the app. If not provided, all +.a+ libraries built will be used.
|
||||
.2+| +: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.
|
||||
| +: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 +"include"+. The default value is the vendor project directory.
|
||||
|====
|
||||
|
||||
As an example, assuming our video player project wants to make use of the OAuth2Client 3rd-library library, a +vendor+ directory would be created and the OAuth2Client source code would be added there.
|
||||
As an example, assuming our video player project wants to make use of the OAuth2Client 3rd-library library, a 'vendor' directory would be created and the OAuth2Client source code would be added there.
|
||||
|
||||
<pre>
|
||||
----
|
||||
$ cd AwesomeVideoPlayer
|
||||
$ ls vendor
|
||||
OAuth2Client
|
||||
</pre>
|
||||
----
|
||||
|
||||
Then, the +Rakefile+ can be modified as such.
|
||||
Then, the 'Rakefile' can be modified as such.
|
||||
|
||||
<pre>
|
||||
----
|
||||
Motion::Project::App.setup do |app|
|
||||
# ...
|
||||
app.vendor_project('vendor/OAuth2Client', :xcode,
|
||||
@@ -132,9 +153,10 @@ Motion::Project::App.setup do |app|
|
||||
:headers_dir => 'Sources/OAuth2Client')
|
||||
app.frameworks << 'Security' # OAuth2Client depends on Security.framework
|
||||
end
|
||||
</pre>
|
||||
----
|
||||
|
||||
h3. Entitlements
|
||||
Entitlements
|
||||
~~~~~~~~~~~~
|
||||
|
||||
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.
|
||||
|
||||
@@ -142,32 +164,33 @@ An application running on an iOS device that does not have the proper entitlemen
|
||||
|
||||
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 the developer can modify to set appropriate keys and values.
|
||||
The +entitlements+ method of the 'Rakefile' configuration object returns an empty +Hash+ object by default, that the developer 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.
|
||||
|
||||
<pre>
|
||||
----
|
||||
Motion::Project::App.setup do |app|
|
||||
# ...
|
||||
app.entitlements['keychain-access-groups'] = [
|
||||
app.seed_id + '.' + app.identifier
|
||||
]
|
||||
end
|
||||
</pre>
|
||||
----
|
||||
|
||||
h3. Advanced Info.plist Settings
|
||||
Advanced Info.plist Settings
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
An iOS app has its configuration described into 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":http://developer.apple.com/library/ios/#documentation/General/Reference/InfoPlistKeyReference/Introduction/Introduction.html guide.
|
||||
An iOS app has its configuration described into 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 http://developer.apple.com/library/ios/#documentation/General/Reference/InfoPlistKeyReference/Introduction/Introduction.html[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 the developer control the necessary settings of his project.
|
||||
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 the developer control the necessary settings of his project.
|
||||
|
||||
However, it might happen that the developer 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.
|
||||
However, it might happen that the developer 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.
|
||||
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.
|
||||
|
||||
<pre>
|
||||
----
|
||||
Motion::Project::App.setup do |app|
|
||||
# ...
|
||||
app.info_plist['CFBundleURLTypes'] = [
|
||||
@@ -175,22 +198,24 @@ Motion::Project::App.setup do |app|
|
||||
'CFBundleURLSchemes' => ['x-videoplayer'] }
|
||||
]
|
||||
end
|
||||
</pre>
|
||||
----
|
||||
|
||||
h2. Build
|
||||
Build
|
||||
-----
|
||||
|
||||
The +rake build+ task builds the project into the temporary +build+ directory. 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.
|
||||
The +rake build+ task builds the project into the temporary 'build' directory. 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.
|
||||
|
||||
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":http://llvm.org/), then assembly. The compiler will generate code for either the Intel 32-bit (+i386+) or ARM (+armv6+, +armv7+) instruction sets and ABIs depending on the target.
|
||||
# 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 been deleted since 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.
|
||||
. It compiles each Ruby source code file into optimized machine code, translating the Ruby syntax tree into an intermediate representation language (using http://llvm.org[LLVM]), then assembly. The compiler will generate code for either the Intel 32-bit (+i386+) or ARM (+armv6+, +armv7+) instruction sets and ABIs depending on the target.
|
||||
. 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 been deleted since 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.
|
||||
Normally the user does not need to explicitly build the project, as the 'build' task is a dependency of the other tasks.
|
||||
|
||||
h3. Multicore Compilation
|
||||
Parallel Compilation
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The compilation of Ruby source files into machine code takes a non-negligeable amount of time.
|
||||
|
||||
@@ -200,21 +225,28 @@ The build system uses the value of the +machdep.cpu.thread_count+ sysctl kernel
|
||||
|
||||
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.
|
||||
|
||||
<pre>
|
||||
----
|
||||
$ rake jobs=1 # Force compilation tasks to be sequential.
|
||||
</pre>
|
||||
----
|
||||
|
||||
h3. Cleaning
|
||||
Cleaning
|
||||
~~~~~~~~
|
||||
|
||||
The +rake clean+ task empties the +build+ directory.
|
||||
The +rake clean+ task empties the 'build' directory and clean the build directories of the vendored projects.
|
||||
|
||||
h2. Simulation
|
||||
Simulation
|
||||
----------
|
||||
|
||||
The +rake simulator+ task builds the project for the iOS simulator, and runs the application in the simulator.
|
||||
|
||||
The default +rake+ task is a shortcut to +rake simulator+.
|
||||
|
||||
h3. Universal Applications
|
||||
----
|
||||
$ rake
|
||||
----
|
||||
|
||||
Universal Applications
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A universal application targets both the iPhone and iPad device families (see the +device_family+ project configuration setting for more details).
|
||||
|
||||
@@ -222,27 +254,29 @@ In this case, it can be convenient to specify which device family the simulator
|
||||
|
||||
The +device_family+ environment variable can be set to either +iphone+ or +ipad+ to change this default. For example, forcing a universal application to run on the iPad.
|
||||
|
||||
<pre>
|
||||
----
|
||||
$ rake simulator device_family=ipad
|
||||
</pre>
|
||||
----
|
||||
|
||||
h3. Cleaning the Sandbox
|
||||
Cleaning the Sandbox
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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 the 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.
|
||||
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, the developer 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.
|
||||
|
||||
<pre>
|
||||
----
|
||||
$ rake simulator clean=1
|
||||
</pre>
|
||||
----
|
||||
|
||||
h2. Testing
|
||||
Testing
|
||||
-------
|
||||
|
||||
RubyMotion has built-in "Behavior-Driven Development":http://en.wikipedia.org/wiki/Behavior_Driven_Development functionality that the developer can use to cover the specifications of an application. RubyMotion uses a flavor of the "Bacon":https://github.com/alloy/MacBacon framework slightly modified to integrate better with the iOS system.
|
||||
RubyMotion has built-in http://en.wikipedia.org/wiki/Behavior_Driven_Development[Behavior-Driven Development] functionality that the developer can use to cover the specifications of an application. RubyMotion uses a flavor of the http://github.com/alloy/MacBacon[Bacon] framework slightly modified to integrate better with the iOS system.
|
||||
|
||||
Specification files are written in the +spec+ directory.
|
||||
Specification files are written in the 'spec' directory.
|
||||
|
||||
The +rake spec+ task will build a special version of the project that will execute all specification files right after the application launches. A summary will then be printed on the standard output terminal, and the application will exit.
|
||||
|
||||
@@ -250,7 +284,7 @@ Note: +rake spec+ currently only works with the simulator.
|
||||
|
||||
Specification files have full access to the iOS SDK as well as the application classes. Here is a spec that makes sure the application has one active window.
|
||||
|
||||
<pre>
|
||||
----
|
||||
$ cat spec/main_spec.rb
|
||||
describe "My Awesome App" do
|
||||
it "has a window" do
|
||||
@@ -258,32 +292,34 @@ describe "My Awesome App" do
|
||||
app.windows.size.should == 1
|
||||
end
|
||||
end
|
||||
</pre>
|
||||
----
|
||||
|
||||
Assuming the application is properly implemented to follow that specification, +rake spec+ will gracefully exit with a status error of 0.
|
||||
|
||||
<pre>
|
||||
----
|
||||
$ rake spec
|
||||
[...]
|
||||
...
|
||||
My Awesome App
|
||||
- has a window
|
||||
|
||||
1 specifications (1 requirements), 0 failures, 0 errors
|
||||
</pre>
|
||||
----
|
||||
|
||||
h2. Archiving
|
||||
Archiving
|
||||
---------
|
||||
|
||||
RubyMotion projects can be archived in order to be distributed and submitted to the App Store.
|
||||
|
||||
h3. Development vs Release
|
||||
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 only 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.
|
||||
|
||||
Sometimes, the developer may 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, which will yield the given block in case the project builds for the appropriate mode.
|
||||
Sometimes, the developer may 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, which will yield the given block in case the project builds for the appropriate mode.
|
||||
|
||||
<pre>
|
||||
----
|
||||
Motion::Project::App.setup do |app|
|
||||
# ...
|
||||
app.development do
|
||||
@@ -291,9 +327,10 @@ Motion::Project::App.setup do |app|
|
||||
app.entitlements['get-task-allow'] = true
|
||||
end
|
||||
end
|
||||
</pre>
|
||||
----
|
||||
|
||||
h3. Install on Device
|
||||
Install on Device
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
The +rake device+ task builds and uploads a development archive of the application to an iOS device.
|
||||
|
||||
@@ -308,10 +345,11 @@ The process will fail in the following cases:
|
||||
|
||||
Otherwise, the process returns successfully and the application is then available on the device springboard.
|
||||
|
||||
h3. Distribution
|
||||
Distribution
|
||||
~~~~~~~~~~~~
|
||||
|
||||
The +rake archive+ task can be used to generate +.ipa+ archives for both development and release modes. An +.ipa+ archive is suitable for ad-hoc distribution.
|
||||
The +rake archive+ task can be used to generate '.ipa' archives for both development and release modes. An '.ipa' archive is suitable for ad-hoc distribution.
|
||||
|
||||
The task requires a valid certificate and provisioning profile in order to codesign the app bundle.
|
||||
|
||||
In release mode, an +.xcarchive+ archive is also created. This file opens with the Xcode 4 organizer and allows the developer to perform an App Store submission.
|
||||
In release mode, an '.xcarchive' archive is also created. This file opens with the Xcode 4 organizer and allows the developer to perform an App Store submission.
|
||||
242
doc/Rakefile
242
doc/Rakefile
@@ -163,249 +163,15 @@ class DocsetGenerator
|
||||
end
|
||||
end
|
||||
|
||||
require 'redcloth'
|
||||
class GuideGenerator < RedCloth
|
||||
def initialize(src, dest, no_header_footer)
|
||||
base = File.basename(src, '.textile')
|
||||
@title = base.gsub(/([a-z])([A-Z])/, '\1 \2')
|
||||
@dest = dest
|
||||
@no_header_footer = no_header_footer
|
||||
data = File.read(src)
|
||||
super(fix_anchors(data))
|
||||
end
|
||||
|
||||
def run
|
||||
File.open(@dest, 'w') do |io|
|
||||
unless @no_header_footer
|
||||
io.puts html_header
|
||||
#io.puts toc
|
||||
io.puts '<div class="content">'
|
||||
end
|
||||
io.puts to_html(:inline_plus_annotations, :textile)
|
||||
unless @no_header_footer
|
||||
io.puts '</div>'
|
||||
io.puts html_footer
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def toc
|
||||
toggle_func = <<EOS
|
||||
if (document.getElementById('toc-content').style.display == 'none') {
|
||||
document.getElementById('toc-content').style.display = 'block';
|
||||
document.getElementById('toc-toggle-link').text = 'hide';
|
||||
}
|
||||
else {
|
||||
document.getElementById('toc-content').style.display = 'none';
|
||||
document.getElementById('toc-toggle-link').text = 'show';
|
||||
}
|
||||
EOS
|
||||
|
||||
text = "<div class=\"toc\"><h3>Table of Contents (<a id=\"toc-toggle-link\" href=\"javascript:void(0)\" onClick=\"#{toggle_func}\">hide</a>)</h3>"
|
||||
current_level = 1
|
||||
text << "<div id=\"toc-content\">"
|
||||
self.to_s.scan(/^h([2-3])\((#[^)]+)\)\.([^\n]+)\n/).each do |level, anchor, title|
|
||||
level = level.to_i
|
||||
if level > current_level
|
||||
text << '<ol>'
|
||||
current_level = level
|
||||
elsif level < current_level
|
||||
text << '</ol>'
|
||||
current_level = level
|
||||
end
|
||||
text << "<li class=\"toc-level#{level}\"><a href=\"#{anchor}\">#{title}</a></li>"
|
||||
end
|
||||
while current_level > 1
|
||||
text << '</ol>'
|
||||
current_level -= 1
|
||||
end
|
||||
text << '</div></div>'
|
||||
return text
|
||||
end
|
||||
|
||||
# Transform +foo+ into <tt>foo</tt>.
|
||||
def inline_plus_annotations(text)
|
||||
text.gsub!(/\+(.*?)\+/m) { |m| rip_offtags("<tt>#{$~[1]}</tt>") }
|
||||
end
|
||||
|
||||
# Auto-define anchors for h[1-4] elements.
|
||||
def fix_anchors(text)
|
||||
text.gsub(/^(h[1-4])\.([^\n]+)/m) do |m|
|
||||
"#{$1}(##{anchor_name($2)}). #{$2}"
|
||||
end
|
||||
end
|
||||
|
||||
def anchor_name(title)
|
||||
title.strip.gsub(/\s/, '-').downcase
|
||||
end
|
||||
|
||||
def css
|
||||
<<EOS
|
||||
body {
|
||||
font-family: "Lucida Sans", "Lucida Grande", Verdana, Arial, sans-serif;
|
||||
font-size: 15px;
|
||||
text-align: left
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-bottom: 20px;
|
||||
margin-left: 280px;
|
||||
margin-right: 280px;
|
||||
margin-top: 0px;
|
||||
position: relative;
|
||||
text-align: left;
|
||||
min-width: 700px;
|
||||
max-width: 900px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
padding-top: 10px;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.4em;
|
||||
border-bottom: 1px #aaa solid;
|
||||
padding-top: 30px;
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
|
||||
.content li {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
a:link, a:active, a:visited {
|
||||
color: #33a;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover, a:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
pre, code {
|
||||
overflow: auto;
|
||||
color: #222;
|
||||
background: #EEE;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
border: 1px solid #CCC;
|
||||
}
|
||||
|
||||
tt {
|
||||
color: #222;
|
||||
/*background: #EEE;*/
|
||||
}
|
||||
|
||||
pre, code, tt {
|
||||
font-size: 14px;
|
||||
font-family: "Anonymous Pro", "Inconsolata", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
table {
|
||||
border: 1px solid #CCC;
|
||||
background: #FFF;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table th, table td {
|
||||
font-size: 15px;
|
||||
padding: 0.25em 1em;
|
||||
border: 1px solid #CCC;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table th {
|
||||
font-size: 15px;
|
||||
background: #EEE;
|
||||
font-weight: bold;
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
|
||||
.toc {
|
||||
margin-left: 20px;
|
||||
padding-left: 15px;
|
||||
padding-right: 20px;
|
||||
float: left;
|
||||
border: 1px solid #ddd;
|
||||
max-width: 195px;
|
||||
-webkit-box-shadow: -2px 2px 6px #bbb;
|
||||
-moz-box-shadow: -2px 2px 6px #bbb;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.toc h3, .toc-level2 {
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.toc-level3 {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.toc-level4 {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.toc ol {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.toc li {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
EOS
|
||||
end
|
||||
|
||||
def javascript
|
||||
<<EOS
|
||||
EOS
|
||||
end
|
||||
|
||||
def html_header
|
||||
<<EOS
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<title>#{@title}</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<style type="text/css">
|
||||
#{css}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
#{javascript}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
EOS
|
||||
end
|
||||
|
||||
def html_footer
|
||||
<<EOS
|
||||
</body>
|
||||
</html>
|
||||
EOS
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
task :default => :all
|
||||
#task :all => [:guides, :docset]
|
||||
task :all => [:guides] # no docset in the main distribution...
|
||||
|
||||
task :guides do
|
||||
devcenter = ENV['devcenter_destdir']
|
||||
Dir.glob('*.textile').each do |src|
|
||||
dest = (File.basename(src, '.textile') + '.html')
|
||||
dest = dest.downcase.gsub(/ /, '-') if devcenter
|
||||
dest = File.join(devcenter, dest) if devcenter
|
||||
if devcenter or !File.exist?(dest) or File.mtime(src) > File.mtime(dest) or File.mtime(__FILE__) > File.mtime(dest)
|
||||
GuideGenerator.new(src, dest, devcenter != nil).run
|
||||
Dir.glob('*.txt').each do |src|
|
||||
dest = (File.basename(src, '.txt') + '.html')
|
||||
if !File.exist?(dest) or File.mtime(src) > File.mtime(dest) or File.mtime(__FILE__) > File.mtime(dest)
|
||||
sh "mizuho -a max-width=55em \"#{src}\" -o \"#{dest}\""
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,66 +1,71 @@
|
||||
h1. Runtime
|
||||
RubyMotion Runtime Guide
|
||||
========================
|
||||
Laurent Sansonetti <lrz@hipbyte.com>
|
||||
|
||||
The RubyMotion runtime implements the Ruby language functionality required during the execution of an application. The object model, builtin classes and memory management system are part of the runtime.
|
||||
|
||||
Althrough similars in appearance, the RubyMotion runtime has a completely different implementation than "CRuby":http://www.ruby-lang.org/en/, the mainstream implementation. We will cover the main differences in the following sections.
|
||||
Althrough similar in appearance, the RubyMotion runtime has a completely different implementation than http://www.ruby-lang.org/en/[CRuby], the mainstream implementation. We will cover the main differences in the following sections.
|
||||
|
||||
The key feature of the RubyMotion runtime is its tight integration with iOS, which makes applications very efficient.
|
||||
The key feature of the RubyMotion runtime is its tight integration with iOS, which makes it suitable to power efficient applications.
|
||||
|
||||
RubyMotion follows the Ruby 1.9 language specifications.
|
||||
|
||||
h2. Object Model
|
||||
Object Model
|
||||
------------
|
||||
|
||||
The object model of RubyMotion is based on "Objective-C":http://en.wikipedia.org/wiki/Objective-C, the underlying language runtime of the iOS SDK. Objective-C is an object-oriented flavor of C that has been, like Ruby, heavily influenced by the "Smalltalk":http://en.wikipedia.org/wiki/Smalltalk language.
|
||||
The object model of RubyMotion is based on http://en.wikipedia.org/wiki/Objective-C[Objective-C], the underlying language runtime of the iOS SDK. Objective-C is an object-oriented flavor of C that has been, like Ruby, heavily influenced by the http://en.wikipedia.org/wiki/Smalltalk[Smalltalk] language.
|
||||
|
||||
Sharing a common ancestor, the object models of Ruby and Objective-C are conceptually similar. For instance, both languages have the notion of open classes, single inheritance, and single dynamic message dispatch.
|
||||
Sharing a common ancestor, the object models of Ruby and Objective-C are conceptually similar. For instance, both languages have the notion of open classes, single inheritance and single dynamic message dispatch.
|
||||
|
||||
RubyMotion implements the Ruby object model by using the "Objective-C runtime":http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html, the library that powers the Objective-C language, and indirectly, the iOS SDK APIs.
|
||||
RubyMotion implements the Ruby object model by using the http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html[Objective-C runtime], the library that powers the Objective-C language and indirectly, the iOS SDK APIs.
|
||||
|
||||
In RubyMotion, Ruby classes, methods and objects are respectively Objective-C classes, methods and objects. And reciprocally, Objective-C classes, methods and objects are available in Ruby as if they were native.
|
||||
|
||||
By sharing the same object model infrastructure, Objective-C and RubyMotion APIs can be interchangeable at no additional performance expense.
|
||||
|
||||
h3. Objective-C Messages
|
||||
Objective-C Messages
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
RubyMotion lets the developer send and define Objective-C messages.
|
||||
|
||||
An Objective-C message, also called a selector, can look different than a typical Ruby message, if it contains more than one argument.
|
||||
|
||||
Unlike Ruby messages, Objective-C messages contain keywords. Each argument has a keyword associated to it, and the final Objective-C message is the combination of all keywords.
|
||||
Unlike Ruby messages, Objective-C messages contain keywords. Each argument has a keyword associated to it and the final Objective-C message is the combination of all keywords.
|
||||
|
||||
Let's take the following piece of Objective-C code which draws a string.
|
||||
|
||||
<pre>
|
||||
----
|
||||
[string drawAtPoint:point withFont:font];
|
||||
</pre>
|
||||
----
|
||||
|
||||
In this code, +string+, +point+ and +font+ are variables. The message keywords are +drawAtPoint:+ and +withFont:+. The complete message is the combination of these keywords, +drawAtPoint:withFont:+ , which is sent to the +string+ object, passing the +point+ and +font+ objects as arguments.
|
||||
|
||||
Objective-C messages can be sent from RubyMotion using a similar syntax.
|
||||
|
||||
<pre>
|
||||
----
|
||||
string.drawAtPoint(point, withFont:font)
|
||||
</pre>
|
||||
----
|
||||
|
||||
It is important to keep in mind that the message being sent here is +drawAtPoint:withFont:+. To illustrate more, the same message can be manually dispatched using the +send+ method.
|
||||
|
||||
<pre>
|
||||
----
|
||||
string.send(:'drawAtPoint:withFont:', point, font)
|
||||
</pre>
|
||||
----
|
||||
|
||||
Objective-C messages can also be defined in RubyMotion. Let's imagine we are building a class that should act as a proxy for our drawing code. The following piece of code defines the +drawAtPoint:withFont:+ message on the +DrawingProxy+ class.
|
||||
|
||||
<pre>
|
||||
----
|
||||
class DrawingProxy
|
||||
def drawAtPoint(point, withFont:font)
|
||||
@str.drawAtPoint(point, withFont:font)
|
||||
end
|
||||
end
|
||||
</pre>
|
||||
----
|
||||
|
||||
Note: The syntax used to define Objective-C selectors was added to RubyMotion and is not part of the Ruby standard.
|
||||
NOTE: The syntax used to define Objective-C selectors was added to RubyMotion and is not part of the Ruby standard.
|
||||
|
||||
h3. Reading Objective-C Interfaces
|
||||
Objective-C Interfaces
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
As Objective-C is the main language used to describe the iOS SDK, it is necessary to understand how to read Objective-C interfaces and how to use them from Ruby.
|
||||
|
||||
@@ -70,58 +75,63 @@ An Objective-C interface starts with either the minus or plus character, which i
|
||||
|
||||
For instance, the following interface declares the +foo+ instance method on the +Foo+ class.
|
||||
|
||||
<pre>
|
||||
----
|
||||
@class Foo
|
||||
- (id)foo;
|
||||
@end
|
||||
</pre>
|
||||
----
|
||||
|
||||
The next one declares the +foo+ class method on the same class.
|
||||
|
||||
<pre>
|
||||
----
|
||||
@class Foo
|
||||
+ (id)foo;
|
||||
@end
|
||||
</pre>
|
||||
----
|
||||
|
||||
Note: +(id)+ is the type information. Types are covered in an upcoming section, so you can omit them for now.
|
||||
NOTE: +(id)+ is the type information. Types are covered in an upcoming section, so you can omit them for now.
|
||||
|
||||
As seen in the previous section, arguments in Objective-C methods can be named with a keyword. The sum of all keywords form the message name, or selector.
|
||||
|
||||
The following interface declares the +doSomethingWithObject:andObject:+ class method on the +Test+ class.
|
||||
|
||||
<pre>
|
||||
----
|
||||
@class Test
|
||||
+ (id)sharedInstanceWithObject:(id)obj1 andObject:(id)obj2;
|
||||
@end
|
||||
</pre>
|
||||
----
|
||||
|
||||
This method can be called from Ruby like this.
|
||||
|
||||
<pre>
|
||||
----
|
||||
# obj1 and obj2 are variables for the arguments.
|
||||
instance = Test.sharedInstanceWithObject(obj1, andObject:obj2)
|
||||
</pre>
|
||||
----
|
||||
|
||||
h3. Shortcuts
|
||||
Selector Shortcuts
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The RubyMotion runtime provides convenience shortcuts for certain Objective-C selectors.
|
||||
|
||||
|_<. Selector |_<. Shortcut |
|
||||
| +setFoo:+ | +foo=+ |
|
||||
| +isFoo+ | +foo?+ |
|
||||
| +objectForKey:+ | +[]+ |
|
||||
| +setObject:forKey:+ | +[]=+ |
|
||||
[options="header,autowidth"]
|
||||
|===============================
|
||||
| Selector | Shortcut
|
||||
| +setFoo:+ | +foo=+
|
||||
| +isFoo+ | +foo?+
|
||||
| +objectForKey:+ | +[]+
|
||||
| +setObject:forKey:+ | +[]=+
|
||||
|===============================
|
||||
|
||||
As an example, the +setHidden:+ and +isHidden+ methods of +UIView+ can be called by using the +hidden=+ and +hidden?+ shortcuts.
|
||||
|
||||
<pre>
|
||||
----
|
||||
view.hidden = true unless view.hidden?
|
||||
</pre>
|
||||
----
|
||||
|
||||
h2. Builtin Classes
|
||||
Builtin Classes
|
||||
---------------
|
||||
|
||||
Some of the builtin classes of RubyMotion are based on the "Foundation framework":http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/ObjC_classic/_index.html, the base layer of iOS.
|
||||
Some of the builtin classes of RubyMotion are based on the http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/ObjC_classic/_index.html[Foundation framework], the base layer of iOS.
|
||||
|
||||
The Foundation framework is an Objective-C framework, but due to the fact that RubyMotion is based on the Objective-C runtime, the classes that it defines can be naturally re-used in RubyMotion.
|
||||
|
||||
@@ -131,51 +141,56 @@ Foundation comes with the root object class, +NSObject+, as well as a set of pri
|
||||
|
||||
Here is a table showing the ancestors chain of a newly created class, +Hello+, as well as some of the Ruby builtin classes.
|
||||
|
||||
|_<. Ruby Class |_<. Ancestors |
|
||||
| +Hello+ | +NSObject+ → +Kernel+ |
|
||||
| +String+ | +NSMutableString+ → +NSString+ → +Comparable+ → +NSObject+ → +Kernel+ |
|
||||
| +Array+ | +NSMutableArray+ → +NSArray+ → +Enumerable+ → +NSObject+ → +Kernel+ |
|
||||
| +Hash+ | +NSMutableDictionary+ → +NSDictionary+ → +Enumerable+ → +NSObject+ → +Kernel+ |
|
||||
| +Numeric+ | +Comparable+ → +NSNumber+ → +NSValue+ → +NSObject+ → +Kernel+ |
|
||||
| +Time+ | +Comparable+ → +NSDate+ → +NSObject+ → +Kernel+ |
|
||||
[options="header,autowidth"]
|
||||
|==================================
|
||||
| Ruby Class | Ancestors
|
||||
| +Hello+ | +NSObject+ → +Kernel+
|
||||
| +String+ | +NSMutableString+ → +NSString+ → +Comparable+ → +NSObject+ → +Kernel+
|
||||
| +Array+ | +NSMutableArray+ → +NSArray+ → +Enumerable+ → +NSObject+ → +Kernel+
|
||||
| +Hash+ | +NSMutableDictionary+ → +NSDictionary+ → +Enumerable+ → +NSObject+ → +Kernel+
|
||||
| +Numeric+ | +Comparable+ → +NSNumber+ → +NSValue+ → +NSObject+ → +Kernel+
|
||||
| +Time+ | +Comparable+ → +NSDate+ → +NSObject+ → +Kernel+
|
||||
|===================================
|
||||
|
||||
A direct consequence of hosting the Ruby builtin classes on Foundation is that instances respond to more messages. For instance, the +NSString+ class defines the +uppercaseString+ method. Since the +String+ class is a subclass of +NSString+, strings created in Ruby also respond to that method.
|
||||
|
||||
<pre>
|
||||
----
|
||||
'hello'.uppercaseString # => 'HELLO
|
||||
</pre>
|
||||
----
|
||||
|
||||
Respectively, the Ruby interface of these builtin classes is implemented on their Foundation counterparts. As an example, +Array+'s +each+ method is implemented on +NSArray+. This allows primitive types to always respond to the same interface, regardless of where they come from. +each+ will always work on all arrays.
|
||||
|
||||
<pre>
|
||||
----
|
||||
def iterate(ary)
|
||||
ary.each { |x| p x }
|
||||
end
|
||||
|
||||
iterate [42]
|
||||
iterate NSArray.arrayWithObject(42)
|
||||
</pre>
|
||||
----
|
||||
|
||||
But the main purpose of this design is to allow the exchange of primitive types between Objective-C and Ruby at no performance cost, since they don't have to be converted. This is important as most of the types that will be exchanged in a typical application are likely going to be builtin types. A +String+ object created in Ruby can have its memory address passed as the argument of an Objective-C method that expects an +NSString+.
|
||||
|
||||
h3. Mutability
|
||||
Mutability
|
||||
~~~~~~~~~~
|
||||
|
||||
The Foundation framework ships a set of classes that have both mutable and immutable variants. Mutable objects can be modified, while immutable objects cannot.
|
||||
|
||||
Immutable Foundation types in RubyMotion behave like frozen objects (objects which received the +freeze+ message).
|
||||
|
||||
As an example, changing the content of an +NSString+ is prohibited, and an exception will be raised by the system if doing so. However, it is possible to change the content of an +NSMutableString+ instance.
|
||||
As an example, changing the content of an +NSString+ is prohibited and an exception will be raised by the system if doing so. However, it is possible to change the content of an +NSMutableString+ instance.
|
||||
|
||||
<pre>
|
||||
----
|
||||
NSString.new.strip! # raises RuntimeError: can't modify frozen/immutable string
|
||||
NSMutableString.new.strip! # works
|
||||
</pre>
|
||||
----
|
||||
|
||||
Strings created in RubyMotion inherit from +NSMutableString+, so they can be modifiedby default. The same goes for arrays and hashes.
|
||||
Strings created in RubyMotion inherit from +NSMutableString+, so they can be modified by default. The same goes for arrays and hashes.
|
||||
|
||||
However, the developer must be careful that it is very common for iOS SDK APIs to return immutable types. In these cases, the +dup+ or +mutableCopy+ messages can be sent to the object in order to get a mutable version of it, that can be modified later on.
|
||||
|
||||
h2. Interfacing with C
|
||||
Interfacing with C
|
||||
------------------
|
||||
|
||||
One does not need to be a C programmer in order to use RubyMotion, however some basic notions, explained in this section, will be required.
|
||||
|
||||
@@ -185,42 +200,47 @@ Also, while Objective-C is the main programming language used in the iOS SDK, so
|
||||
|
||||
RubyMotion comes with an interface that allows Ruby to deal with the C part of APIs.
|
||||
|
||||
h3. Basic Types
|
||||
Basic Types
|
||||
~~~~~~~~~~~
|
||||
|
||||
C, and indirectly Objective-C, has a set of basic types. It is common in iOS SDK APIs to accept or return these types.
|
||||
|
||||
An example are the following methods of +NSFileHandle+, which both respectively accept and return the C integer type, +int+.
|
||||
|
||||
<pre>
|
||||
----
|
||||
- (id)initWithFileDescriptor:(int)fd;
|
||||
- (int)fileDescriptor;
|
||||
</pre>
|
||||
----
|
||||
|
||||
Basic C types cannot be created from Ruby directly, but are automatically converted from and to equivalent Ruby types.
|
||||
|
||||
The following piece of code uses the two +NSFileHandle+ methods described above. The first one, +initWithFileDescriptor:+, is called by passing a +Fixnum+. The second one, +fileDescriptor+, is called and a +Fixnum+ is returned back to Ruby.
|
||||
|
||||
<pre>
|
||||
----
|
||||
handle = NSFileHandle.alloc.initWithFileDescriptor(2)
|
||||
handle.fileDescriptor # => 2
|
||||
</pre>
|
||||
----
|
||||
|
||||
This table describes all basic C types and discusses how they are converted from and to Ruby types.
|
||||
|
||||
|_<. C Type |_<. From Ruby to C |_<. From C to Ruby |
|
||||
| +bool+ |/2. If the object is +false+ or +nil+, +false+, otherwise, +true+. Note: the +0+ fixnum will evaluate to +true+. |/2. Either +true+ or +false+. |
|
||||
| +BOOL+ |||
|
||||
| +char+ |/5. If the object is a +Fixnum+ or a +Bignum+, the value is returned. If the object is +true+ or +false+, +1+ or +0+ are respectively returned. If the object responds to the +to_i+ message, it is sent and the result is returned.|/5. Either a +Fixnum+ or a +Bignum+ object.|
|
||||
| +short+ |||
|
||||
| +int+ |||
|
||||
| +long+ |||
|
||||
| +long_long+ |||
|
||||
| +float+ |/2. If the object is a +Float+, the value is returned. If the object is +true+ or +false+, +1.0+ or +0.0+ are respectively returned. If the object responds to the +to_f+ message, it is sent and the result is returned.|/2. A +Float+ object.|
|
||||
| +double+ |||
|
||||
[options="header,autowidth"]
|
||||
|=========================================
|
||||
| C Type | From Ruby to C | From C to Ruby
|
||||
| +bool+ .2+.^| If the object is +false+ or +nil+, +false+, otherwise, +true+. Note: the +0+ fixnum will evaluate to +true+. .2+.^| Either +true+ or +false+.
|
||||
| +BOOL+
|
||||
| +char+ .5+.^| If the object is a +Fixnum+ or a +Bignum+, the value is returned. If the object is +true+ or +false+, +1+ or +0+ are respectively returned. If the object responds to the +to_i+ message, it is sent and the result is returned. .5+.^| Either a +Fixnum+ or a +Bignum+ object.
|
||||
| +short+
|
||||
| +int+
|
||||
| +long+
|
||||
| +long_long+
|
||||
| +float+ .2+.^| If the object is a +Float+, the value is returned. If the object is +true+ or +false+, +1.0+ or +0.0+ are respectively returned. If the object responds to the +to_f+ message, it is sent and the result is returned. .2+.^| A +Float+ object.
|
||||
| +double+
|
||||
|=========================================
|
||||
|
||||
When using an API that returns the +void+ C type, RubyMotion will return +nil+.
|
||||
|
||||
h3. Structures
|
||||
Structures
|
||||
~~~~~~~~~~
|
||||
|
||||
C structures are mapped to classes in RubyMotion. Structures can be created in Ruby and passed to APIs expecting C structures. Similarly, APIs returning C structures will return an instance of the appropriate structure class.
|
||||
|
||||
@@ -228,80 +248,83 @@ A structure class has an accessor method for each field of the C structure it wr
|
||||
|
||||
As an example, the following piece of code creates a +CGPoint+ structure, sets its +x+ and +y+ fields, then passes it to the +drawAtPoint:withFont:+ method.
|
||||
|
||||
<pre>
|
||||
----
|
||||
pt = CGPoint.new
|
||||
pt.x = 100
|
||||
pt.y = 200
|
||||
'Hello'.drawAtPoint(pt, withFont: font)
|
||||
</pre>
|
||||
----
|
||||
|
||||
It is possible to pass the field values directly to the constructor.
|
||||
|
||||
<pre>
|
||||
----
|
||||
pt = CGPoint.new(100, 200)
|
||||
'Hello'.drawAtPoint(pt, withFont: font)
|
||||
</pre>
|
||||
----
|
||||
|
||||
RubyMotion will also accept arrays as a convenience. They must contain the same number and type of objects expected in the structure.
|
||||
|
||||
<pre>
|
||||
----
|
||||
'Hello'.drawAtPoint([100, 200], withFont: font)
|
||||
</pre>
|
||||
----
|
||||
|
||||
h3. Enumerations and Constants
|
||||
Enumerations and Constants
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
C enumerations and constants are mapped as constants of the +Object+ class.
|
||||
|
||||
For instance, both +NSNotFound+ and +CGRectNull+, respectively an enumeration and a constant defined by Foundation, can be directly accessed.
|
||||
|
||||
<pre>
|
||||
----
|
||||
if ary.indexOfObject(obj) == NSNotFound
|
||||
# ...
|
||||
end
|
||||
# ...
|
||||
view = UIView.alloc.initWithFrame(CGRectNull)
|
||||
</pre>
|
||||
----
|
||||
|
||||
Note: some enumerations or constants defined in the iOS SDK may start with a lower-case letter, like +kCLLocationAccuracyBest+ (starting with +k+). Because Ruby constants must always start with a capital letter, their names must be corrected by changing the case of the first letter, becoming +KCLLocationAccuracyBest+ (starting with +K+) in Ruby.
|
||||
NOTE: Some enumerations or constants defined in the iOS SDK may start with a lower-case letter, like +kCLLocationAccuracyBest+ (starting with +k+). Because Ruby constants must always start with a capital letter, their names must be corrected by changing the case of the first letter, becoming +KCLLocationAccuracyBest+ (starting with +K+) in Ruby.
|
||||
|
||||
<pre>
|
||||
----
|
||||
locationManager.desiredAccuracy = kCLLocationAccuracyBest # NameError: undefined local variable or method
|
||||
locationManager.desiredAccuracy = KCLLocationAccuracyBest # works
|
||||
</pre>
|
||||
----
|
||||
|
||||
h3. Functions
|
||||
Functions
|
||||
~~~~~~~~~
|
||||
|
||||
C functions are available as methods of the +Object+ class. Inline functions, which are implemented in the framework header files, are also supported.
|
||||
C functions are available as methods of the +Object+ class. Inline functions, which are implemented in the framework header files are also supported.
|
||||
|
||||
As an example, the +CGMakePoint+ function can be used in Ruby to create a +CGRect+ structure.
|
||||
|
||||
<pre>
|
||||
----
|
||||
pt = CGMakePoint(100, 200)
|
||||
'Hello'.drawAtPoint(pt, withFont: font)
|
||||
</pre>
|
||||
----
|
||||
|
||||
Note: most functions in the iOS SDK start by a capital letter. For those who accept no argument, it is important to explicitely use parentheses when calling them, in order to avoid the expression to be evaluated as a constant lookup.
|
||||
NOTE: Most functions in the iOS SDK start by a capital letter. For those who accept no argument, it is important to explicitely use parentheses when calling them, in order to avoid the expression to be evaluated as a constant lookup.
|
||||
|
||||
<pre>
|
||||
----
|
||||
NSHomeDirectory # NameError: uninitialized constant NSHomeDirectory
|
||||
NSHomeDirectory() # works
|
||||
</pre>
|
||||
----
|
||||
|
||||
h3. Pointers
|
||||
Pointers
|
||||
~~~~~~~~
|
||||
|
||||
Pointers are a very basic data type of C. Conceptually, a pointer is a memory address that can point to an object.
|
||||
Pointers are a very basic data type of the C language. Conceptually, a pointer is a memory address that can point to an object.
|
||||
|
||||
In the iOS SDK, pointers are typically used as arguments to return objects by reference. As an example, the +error+ argument of this +NSData+ method expects a pointer that will be set to an +NSError+ object in case of failure.
|
||||
|
||||
<pre>
|
||||
----
|
||||
- (BOOL)writeToFile:(NSString *)path options:(NSDataWritingOptions)mask error:(NSError **)errorPtr
|
||||
</pre>
|
||||
----
|
||||
|
||||
RubyMotion introduces the +Pointer+ class in order to create and manipulate pointers. The type of the pointer to create must be provided in the +new+ constructor. A pointer instance responds to +[]+ to dereference its memory address, and +[]=+ to assign it to a new value.
|
||||
RubyMotion introduces the +Pointer+ class in order to create and manipulate pointers. The type of the pointer to create must be provided in the +new+ constructor. A pointer instance responds to +[]+ to dereference its memory address and +[]=+ to assign it to a new value.
|
||||
|
||||
The +NSData+ method above can be used like this in Ruby.
|
||||
|
||||
<pre>
|
||||
----
|
||||
# Create a new pointer to the object type.
|
||||
error_ptr = Pointer.new(:object)
|
||||
|
||||
@@ -312,38 +335,42 @@ unless data.writeToFile(path, options: mask, error: error_ptr)
|
||||
# Now we can use the `error' object.
|
||||
$stderr.puts "Error when writing data: #{error}"
|
||||
end
|
||||
</pre>
|
||||
----
|
||||
|
||||
In the case above, we need a pointer to an object. +Pointer.new+ can also be used to create pointers to various types, including the basic C types, but also structures.
|
||||
|
||||
+Pointer.new+ accepts either a +String+, which should be one of the "Objective-C runtime types":http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html, or a +Symbol+, which should be a shortcut. We recommend the use of shortcuts.
|
||||
+Pointer.new+ accepts either a +String+, which should be one of the http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html[Objective-C runtime types] or a +Symbol+, which should be a shortcut. We recommend the use of shortcuts.
|
||||
|
||||
Following is a table summarizing the pointers one can create.
|
||||
|
||||
|_<. C Type Pointer |_<. Runtime Type String |_<. Shortcut Symbol |
|
||||
| +id * + | +'@'+ | +:object+ |
|
||||
| +char * + | +'c'+ | +:char+ |
|
||||
| +unsigned char * + | +'C'+ | +:uchar+ |
|
||||
| +short * + | +'s'+ | +:short+ |
|
||||
| +unsigned short * + | +'s'+ | +:ushort+ |
|
||||
| +int * + | +'i'+ | +:int+ |
|
||||
| +unsigned int * + | +'I'+ | +:uint+ |
|
||||
| +long * + | +'l'+ | +:long+ |
|
||||
| +unsigned long * + | +'L'+ | +:ulong+ |
|
||||
| +long long * + | +'q'+ | +:long_long+ |
|
||||
| +unsigned long long * + | +'Q'+ | +:ulong_long+ |
|
||||
| +float * + | +'f'+ | +:float+ |
|
||||
| +double * + | +'d'+ | +:double+ |
|
||||
[cols="m,m,m",options="header,autowidth"]
|
||||
|=======================================================
|
||||
| C Type Pointer | Runtime Type String | Shortcut Symbol
|
||||
| id * | "@" | :object
|
||||
| char * | "c" | :char
|
||||
| unsigned char * | "C" | :uchar
|
||||
| short * | "s" | :short
|
||||
| unsigned short * | "s" | :ushort
|
||||
| int * | "i" | :int
|
||||
| unsigned int * | "I" | :uint
|
||||
| long * | "l" | :long
|
||||
| unsigned long * | "L" | :ulong
|
||||
| long long * | "q" | :long_long
|
||||
| unsigned long long * | "Q" | :ulong_long
|
||||
| float * | "f" | :float
|
||||
| double * | "d" | :double
|
||||
|=======================================================
|
||||
|
||||
RubyMotion supports the creation of structure pointers, by passing their runtime type accordingly to +Pointer.new+, which can be retrieved by sending the +type+ message to the structure class in question. For instance, the following snippet creates a pointer to a +CGRect+ structure.
|
||||
|
||||
<pre>
|
||||
----
|
||||
rect_ptr = Pointer.new(CGRect.type)
|
||||
</pre>
|
||||
----
|
||||
|
||||
Pointers to C characters, also called C strings, are automatically converted from and to +String+ objects by RubyMotion.
|
||||
|
||||
h3. Function Pointers and Blocks
|
||||
Function Pointers and Blocks
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
C or Objective-C APIs accepting C function pointers or C blocks can be called by RubyMotion, by passing a +Proc+ object instead.
|
||||
|
||||
@@ -351,103 +378,106 @@ Functions pointers are pretty rare in the iOS SDK, but C blocks are common. C bl
|
||||
|
||||
As an example, let's consider the +addObserverForName:object:queue:usingBlock:+ method of +NSNotificationCenter+, which accepts a C block as its last argument.
|
||||
|
||||
<pre>
|
||||
----
|
||||
- (id)addObserverForName:(NSString *)name object:(id)obj queue:(NSOperationQueue *)queue
|
||||
usingBlock:(void (^)(NSNotification *))block;
|
||||
</pre>
|
||||
----
|
||||
|
||||
The block in question returns +void+ and accepts one argument, a +NSNotification+ object.
|
||||
|
||||
This method can be called from Ruby like this.
|
||||
|
||||
<pre>
|
||||
----
|
||||
notification_center.addObserverForName(name, object:object, queue:queue,
|
||||
usingBlock:lambda do |notification|
|
||||
# Handle notification here...
|
||||
end)
|
||||
</pre>
|
||||
----
|
||||
|
||||
The +enumerateObjectsWithOptions:usingBlock:+ method of +NSArray+ presents a more complicated case of a C block, which accepts 3 arguments: the enumerated object, its index position in the array, and a pointer to a boolean variable that can be set to +true+ to stop the enumeration.
|
||||
The +enumerateObjectsWithOptions:usingBlock:+ method of +NSArray+ presents a more complicated case of a C block, which accepts 3 arguments: the enumerated object, its index position in the array and a pointer to a boolean variable that can be set to +true+ to stop the enumeration.
|
||||
|
||||
<pre>
|
||||
----
|
||||
- (void)enumerateObjectsWithOptions:(NSEnumerationOptions)opts
|
||||
usingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block;
|
||||
</pre>
|
||||
----
|
||||
|
||||
Assuming we want to stop at the 10th iteration:
|
||||
|
||||
<pre>
|
||||
----
|
||||
ary.enumerateObjectsWithOptions(options, usingBlock:lambda do |obj, idx, stop_ptr|
|
||||
stop_ptr[0] = true if idx == 9
|
||||
end)
|
||||
</pre>
|
||||
----
|
||||
|
||||
(Obviously this example is used for educational purposes, in RubyMotion there is no need to use this method, since +NSArray+ responds to the Ruby +Array+ interface which is must more elaborated.)
|
||||
|
||||
A few important notes:
|
||||
NOTE: The +Proc+ object must have the same number of arguments than the C function pointer or block, otherwise an exception will be raised at runtime.
|
||||
|
||||
# The +Proc+ object must have the same number of arguments than the C function pointer or block, otherwise an exception will be raised at runtime.
|
||||
# In case of a C function pointer argument, you need to make sure the +Proc+ object will not be prematurely collected. If the method will call the function pointer asynchronously, a reference to the +Proc+ object must then be kept. Memory management is discussed in the following section.
|
||||
NOTE: In case of a C function pointer argument, you need to make sure the +Proc+ object will not be prematurely collected. If the method will call the function pointer asynchronously, a reference to the +Proc+ object must then be kept. Memory management is discussed in the following section.
|
||||
|
||||
h2. Memory Management
|
||||
Memory Management
|
||||
-----------------
|
||||
|
||||
RubyMotion provides automatic memory management; the developer does not need to reclaim unused objects.
|
||||
|
||||
Since memory can be limited on iOS hardware, the developer must be careful about not creating large object graphs.
|
||||
|
||||
RubyMotion implements a form of garbage collection called reference counting. An object has an initial reference count of zero, is incremented when a reference to it is created, and decremented when a reference is destroyed. When the count reaches zero, the object's memory is reclaimed by the collector.
|
||||
RubyMotion implements a form of garbage collection called reference counting. An object has an initial reference count of zero, is incremented when a reference to it is created and decremented when a reference is destroyed. When the count reaches zero, the object's memory is reclaimed by the collector.
|
||||
|
||||
RubyMotion's memory management system is designed to simplify the development process. Unlike traditional Objective-C programming, object references are automatically created and destroyed by the system. It is very similar to Objective-C's ARC, in design, but it is differently implemented.
|
||||
|
||||
Object cycles, when two or more objects refer to each other, are currently not handled, but will be in future releases.
|
||||
NOTE: Object cycles, when two or more objects refer to each other, are currently not handled by the runtime, but will be in future releases.
|
||||
|
||||
h3. Objective-C and CoreFoundation Objects
|
||||
NOTE: Object collections are triggered by the system and are not deterministic. RubyMotion is based on the existing autorelease pool infrastructure. iOS creates and destroys autorelease pools for the developer, for instance within the event run loop.
|
||||
|
||||
Objective-C and CoreFoundation Objects
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Objects created by Objective-C or CoreFoundation-style APIs are automatically managed by RubyMotion. There is no need to send the +retain+, +release+ or +autorelease+ messages to them, or use the +CFRetain+ or +CFRelease+ functions.
|
||||
|
||||
The following piece of code allocates two +NSDate+ objects using different constructions. In typical Objective-C, the returned objects would need to be differently managed. In RubyMotion, both objects will be entitled to garbage-collection.
|
||||
|
||||
<pre>
|
||||
----
|
||||
date1 = NSDate.alloc.init
|
||||
date2 = NSDate.date
|
||||
</pre>
|
||||
----
|
||||
|
||||
In order to prevent an object from being collected, a reference must be created to it, such as setting an instance variable to it. This will make sure the object will be kept alive as long as the instance variable receiver is alive.
|
||||
|
||||
<pre>
|
||||
----
|
||||
@date = date1 # date1 will not be collected until the instance variable receiver is collected
|
||||
</pre>
|
||||
----
|
||||
|
||||
There are other ways of creating references to an object, like setting a constant, using a class variable, adding the object to a container (such as +Array+ or +Hash+), and more. Objects created in RubyMotion and passed to Objective-C APIs will also be properly referenced if needed.
|
||||
There are other ways of creating references to an object, like setting a constant, using a class variable, adding the object to a container (such as +Array+ or +Hash+) and more. Objects created in RubyMotion and passed to Objective-C APIs will also be properly referenced if needed.
|
||||
|
||||
h3. Collection Times
|
||||
Immediate Types
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Object collections are triggered by the system and are not deterministic. RubyMotion is based on the existing autorelease pool infrastructure. iOS creates and destroys autorelease pools for the developer, for instance within the event run loop.
|
||||
|
||||
h3. Immediate Types
|
||||
|
||||
The +Fixnum+ and +Float+ types in RubyMotion are immediates; they don't use memory resources to be created. The runtime uses a technique called "tagged pointers":http://en.wikipedia.org/wiki/Tagged_pointer to implement this.
|
||||
The +Fixnum+ and +Float+ types in RubyMotion are immediates; they don't use memory resources to be created. The runtime uses a technique called http://en.wikipedia.org/wiki/Tagged_pointer[tagged pointers] to implement this.
|
||||
|
||||
The ability to use these types without affecting the memory usage is important as a sizable part of a real-world app is spent in control drawing, which can make intensive use of arithmetic algorithms.
|
||||
|
||||
h2. Concurrency
|
||||
Concurrency
|
||||
-----------
|
||||
|
||||
The ability to run code concurrently became critical as multicore processors appeared on iOS devices. RubyMotion has been designed around this purpose.
|
||||
|
||||
RubyMotion has the concept of virtual machine objects, which wrap the state of a thread of execution. A piece of code is running through a virtual machine.
|
||||
|
||||
Virtual machines don't have locks, and there can be multiple virtual machines running at the same time, concurrently.
|
||||
Virtual machines don't have locks and there can be multiple virtual machines running at the same time, concurrently.
|
||||
|
||||
Unlike some Ruby implementations, race conditions are possible in RubyMotion, since there is no "Global Interpreter Lock":http://en.wikipedia.org/wiki/Global_Interpreter_Lock (GIL) to prohibit threads from running concurrently. The developer must be careful to secure concurrent access to shared resources.
|
||||
NOTE: Unlike the mainstream Ruby implementation, http://en.wikipedia.org/wiki/Race_condition#Computing[race conditions] are possible in RubyMotion, since there is no http://en.wikipedia.org/wiki/Global_Interpreter_Lock[Global Interpreter Lock] (GIL) to prohibit threads from running concurrently. The developer must be careful to secure concurrent access to shared resources.
|
||||
|
||||
Different options are available to write concurrent code in RubyMotion.
|
||||
|
||||
h3. Threads and Mutexes
|
||||
Threads and Mutexes
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The +Thread+ class spawns a new POSIX thread that will run concurrently with other threads. The +Mutex+ class wraps a POSIX mutex, and can be used to isolate concurrent access to a shared resource.
|
||||
The +Thread+ class spawns a new POSIX thread that will run concurrently with other threads. The +Mutex+ class wraps a POSIX mutex and can be used to isolate concurrent access to a shared resource.
|
||||
|
||||
h3. Grand Central Dispatch
|
||||
Grand Central Dispatch
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
RubyMotion wraps the "Grand Central Dispatch":http://developer.apple.com/library/ios/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html (GCD) concurrency library under the +Dispatch+ module. It is possible to execute both synchronously and asynchronously blocks of code under concurrent or serial queues.
|
||||
RubyMotion wraps the http://developer.apple.com/library/ios/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html[Grand Central Dispatch] (GCD) concurrency library under the +Dispatch+ module. It is possible to execute both synchronously and asynchronously blocks of code under concurrent or serial queues.
|
||||
|
||||
Albeit more complicated to comprehend than regular threads, sometimes GCD offers a more elegant way to run code concurrently. GCD maintains for the developer a pool of threads and its APIs are architectured to avoid the need to use mutexes.
|
||||
1131
doc/redcloth.rb
1131
doc/redcloth.rb
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user