Quick start – creating your first application

It's finally time to get your hands dirty. Let's cover the structure of the application and then use tests to write your first app. This is where you will use the Red-Green-Refactor to cleanly, and finally, get that magic moment of having your creation come alive on the screen. With your own specs, you'll fully understand each step along the way.

Step 1 – understanding the environment

In this section you will extend the Objective-C delegates and tie into the framework to create a clean and simple RubyMotion application! Delegates/messages are the key to dealing with iOS development.

Cocoa Touch

If you think you can get by with only your Ruby knowledge, you're going to have a hard time. It's important to understand the Cocoa framework, so you can clearly organize your application around it. Building with a framework is completely different than simply writing an application from scratch.

The way most people are taught to code is by using a software library. In a software library, you'll generally make a command call into the library whenever there's some portion of the application you're looking to run. Cocoa, however, is a framework. It's very important to understand that Cocoa Touch calls back into your code at specific points during the application by events that are completely outside your control. You're simply able to interact when the framework calls into your code using the delegate pattern.

Delegates or messages, call code functions that match their parameter signature, upon the correct event.

One analogy to this interaction is sheet music. If you write music and someone is playing it alone, the pauses and notes are all in the sheet music itself. Now take that situation and put that person in an orchestra with others and a master conductor. Though, they will still primarily perform based on your sheet music, they will receive instructions and behave by the ultimate coordination given by the conductor! The conductor keeps everyone playing nicely together, and can even interrupt your previously implied musical timing. Your program depends on instructions from the conductor who interfaces with each musician.

Cocoa and Objective-C are all geared around this message system that rests on the framework. When a conductor looks to your application, he/she will give specific queues that your application should execute the instructions associated, without any knowledge of your application's purpose.

Step 2 – understanding the app structure

It's time to understand the app structure from a RubyMotion perspective. To generate your first ever RubyMotion app, jump into the console and type the following command:

$ motion create –-template=ios HelloWorld

The motion create command then generates a project for you (much like Rails) with the same name you pass the command. In this case, we're creating a project named HelloWorld. Your output should look like the following screenshot:

Step 2 – understanding the app structure

Let's take a look at what files are created in this new HelloWorld project folder.

Here's a breakdown of the generated files:

  • .gitignore: As previously stated, Ruby is strongly coupled with source control, and Ruby developers are especially fans of Git. This is simply more evidenced by the fact that a new project generates a .gitignore file. This file tells Git (should you decide to use it) what files to ignore from any source code repository. This list of intentionally untracked files has been curtailed toward common RubyMotion dynamics.
  • Rakefile: Rake, which is "Ruby make", is a command to build Ruby projects, configured by the Rakefile. Here is where the basic commands for your application build are created. It is also where you'll add as you adjust your features of your program, like what version of the iOS framework you are targeting (or if you're building for OS X instead), or tracking your version number, and so on.
  • app (folder): Here's where your application begins. All application code will exist in this folder or some subfolder therein.
  • app_delegate.rb: Here's the true starting point file of the entire application by default. If you run rake config, you will see the delegate_class is set to AppDelegate and will therefore start here. Changing these rake configurations (in Rakefile) will override these defaults. We will dig into the contents of this file soon.
  • resources (folder): This folder contains any asset you may need in your final application. Generally, it is filled with images, but can contain any resource (.json files, videos, audio, fonts, and so on).
  • [email protected]: Due to the iPhone 5, with the 4-inch screen, the way any application identifies itself as being able to handle the iPhone 5 resolution, is to supply an image of that resolution on startup. So, by default, a large black image of the correct resolution is loaded for you in the resources folder. This will automatically render our application in full resolution for any 4-inch screen. This image is usually completely replaced by a load screen unique to your application.
  • spec (folder): This folder is the predesignated folder for holding all application tests. As you will surely come to see, the Ruby community is strongly coupled with tests, so it should be no surprise that your app generator stubs this step out for you. Tests are written in a lightweight Rspec clone called MacBacon.
  • main_spec.rb: Your default test performs the most basic test possible. It simply validates that your program actually provides a window when run. Also, it is important to note that this test will fail with the generated code, which we will see in the next section.

A short editorial about MacBacon

As stated, MacBacon is a small Rspec clone, and is therefore geared toward behavior-driven development. MacBacon claims less than 350 LoC and allows for an extensive amount of testing. If you come from the Ruby world, you may have your own testing preferences, but in RubyMotion, you'll have to exist with this one alone (for now). It's geared to handle all iOS you'll be throwing at it.

You're almost done, but before you close out this section on the app structure, take a look at the generated app_delegate.rb file in the app folder. When you open this file, you're presented with a very simple five-line Ruby file. Let's review the structure of the generated file.

A short editorial about MacBacon

Line one give us the class that the rake command will be looking for the app delegate, and as you can clearly see they have initialized it with a single method. This method might appear confusing to any Ruby developer who has not familiarized themselves with the named parameter syntax, introduced in Ruby 2.0, but for all intents and purposes this is how RubyMotion has matched the expected Cocoa delegate. At this point Cocoa will run any code placed in here when the application has finished launching. In this case, that code is simply returning true.

To keep with our conductor analogy, the conductor has indicated that the application is ready to start and those who have music in front of them for this portion should begin. Our "music" is to return true... might not even have to learn our scales for that!

Note

If you're not familiar with named parameters don't fret! This is the perfect opportunity for you to become accustomed to using them. Additionally, MacRuby/RubyMotion has been emulating named parameters via hash since before Ruby 2.0 was officially released. This means your new knowledge is instantly backwards compatible in this realm of development (though order must be enforced to match method signatures). At the time of writing, RubyMotion was based on Ruby 1.9, but has announced their intention to move toward Ruby 2.0 compliance.

Step 3 – running and testing the application

Now we have a completely functional application out the box. It doesn't do much; it doesn't even have a window. Let's take a quick moment to look at how to test and run this empty shell program.

If you're familiar with red/green/refactor test-driven development (TDD), we need to see the tests fail before we fix them. We'll be following the red/green/refactor methodology for the projects in this book. All tests will be written first, and will fail because there is no corresponding code. We'll write as much code as needed to get our test Green, aka passed, and then touch up our code with the Refactory cycle. More information on red/green/refactor can be found at http://www.jamesshore.com/Blog/Red-Green-Refactor.html.

As previously stated, our default test is verifying a window exists, and this should fail as there's no code for a window. To run the MacBacon specifications, we simply need to type the following command into the terminal:

$ rake spec

The preceding command will open a small window and run the tests. The parameter is necessary, unlike Rails. Your test runs the actual application in the simulator while the console gives you output relatively similar to the following:

Step 3 – running and testing the application

There's more to the screen, but this and the summary at the end of the tests are the important parts. We've failed our test, which means the test is working (not to be confused with an error in our tests!). We're officially at red in the life cycle.

To add a window to the app, we'll need to edit the app_delegate.rb file and add a single line. Take a look at the following code, where we add line three:

Step 3 – running and testing the application

This single line shouldn't take much explaining. We created an instance variable to hold our created window. We use the iOS window class UIWindow and we instantiate an instance using alloc.initWithFrame.

Note

If you're completely unfamiliar with Objective-C, the alloc portion simply allocates memory for the object, where the follow up is the initializer.

This initializer is called initWithFrame and as a parameter we're passing in the full screen's bounds:

  • @window – is the Awesome Ruby variable holding our window.
  • UIWindow.alloc.initWithFrame – is the Objective-C UIKit framework initialized for the window with a given frame dimension.
  • UIScreen.mainScreen.bounds – is the Objective-C UIKit framework UIScreen.mainScreen returns a UIScreen object and on that we call bounds that returns the position and size of the device's screen as a screen object, which is then consumed by the initializer of UIWindow.

Quite simply we've used Ruby to give us access to native Objective-C objects. You'll see this quite often as Ruby brushes up against the framework verbosity.

Once the preceding line is saved, re-running our tests yields a passing grade! Our code is now considered Green!

Step 3 – running and testing the application

The last step is to refactor. There's not much we can do here, since we've got a shell application, but we can go just a little further into what the application probably should do. I've added the following line to our app_delegate, on the line with @window.makeKeyAndVisible.

Step 3 – running and testing the application

What does this line do? Well you can tell it's an Objective-C function, since it's CamelCased. In iOS, the key window receives the keyboard input. The makeKeyAndVisible method brings the focus to this window as the main window, and assures that the keyboard input should be directed to it. Not much of a refactor but it's a necessary step for most applications, so we'll have it here. You're likely to have this line in just about every application you write.

At this point you can run the actual application, not simply the tests. This can be done by running the rake command with no parameters. By default this will usually run in a very small non-retina window. At the very least, I will usually run a simulator of the 4S retina:

$ rake retina=4     #runs iPhone 5 simulator
$ rake retina=3.5   #runs the iPhone 4S and older simulator

Note

Running rake with no parameters is a shortcut of running rake simulator.

Upon running the code, your window stays open this time, and the console gives you a prompt that simply says (main)>. The prompt is one of the most impressive features of RubyMotion. Your empty shell application is running and you have direct access to your application in what's known as the REPL (Read-Evaluate-Print Loop). Via this command line you can interact directly with your running applications. We'll cover more about how to use this in the Top 3 features you need to know about section.

Step 4 – writing "Hello World" the RubyMotion way

If you've ever opened a computer programming book, you're surely familiar with the classic "Hello World" application. The classic Hello World program simply displays the text from your application. Let's get started!

Since we're driving our application with desired behavior and tests, let's start there and see if we can add another feature and can get to stage Red. Our goal is to add our custom code to the running application window. Following the framework for iOS, this means we'll need to set the window's rootViewController with our own controller. I can think of two simple tests that would verify this. First, to check if the root view is set at all, and second, we can verify that the root view is set to our custom controller, which will be HelloWorldController.

I've written the tests in the supplied test file for simplicity. The tests will make your main_spec.rb file look like the following screenshot:

Step 4 – writing "Hello World" the RubyMotion way

The preceding code adds one line to the before block, and the two additional specifications we discussed previously. Those familiar with before blocks know it helps keep variables current, and even better you don't have to repeat any setup, so your code is DRY (Don't Repeat Yourself).

In line four, we're adding an instance variable to simplify our tests. We'll use this @window variable to both of our follow up tests.

Starting at line 11 we add a check to verify that rootViewController is set, and on line 15 we check its class to see if it's been set to the correct class. By looking at the code, there is little coding explanation needed. MacBacon reads very similar to English! Just like the checking of a window size, these tests are quite constrained and basic, but that's where we're at with our application, so it's nice to see the code to verify these aspects.

Running the specifications at this point will fail, giving us the correct Red status of our development pattern.

We're going to start by making a file to be our controller and placing it inside the app folder. We'll name the file hello_world_controller.rb, so that it can be a class called HelloWorldController, which will satisfy our requirement. Additionally, it will need to have a subclass UIViewController in order to be our rootViewController. Your shell controller should look as such:

Step 4 – writing "Hello World" the RubyMotion way

This is just your normal Ruby class definition subclassing on the iOS class we will be inheriting from.

Now modify our app_delegate.rb file to set our new class to rootViewController, as shown in the following code:

Step 4 – writing "Hello World" the RubyMotion way

On line four we've set the controller to our custom controller. The .new code is generally a shortcut for .alloc.init. Since we didn't have any special initialization, the .new code will work perfectly here. If you're looking to strictly keep Cocoa with clear Cocoa conventions, and delineate clearly between any RubyMotion usages, you may elect to keep using .alloc.init instead.

At this point we've created our controller and set it where we are supposed to. This should be more than enough to pass our tests, so running rake spec will show us that we're now Green! No room to refactor on this round, so the cycle is complete.

The good news is that our controller is in place so we're breaking away from the lifeless shell application and moving towards a true "Hello World" app. We won't need to deal with our AppDelegate any longer.

Now we can start placing code in our controller. Remember when I said that this is a framework that calls into our code at given points, here's where we choose one of those points in time to hook into, and we'll choose one of the most common UIViewController methods, viewDidLoad. So, create a method called viewDidLoad, and let's put the following code inside:

Step 4 – writing "Hello World" the RubyMotion way

Note

To find out what delegates are supported by any method and the order of method calls, you can look up the class reference of the object you are extending. For example, the documentation on the UIViewController class' viewDidLoad call is identified at http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/Reference/Reference.html.

Running your application at this point (by typing rake in the console) will cause your application to output "Hello World" to the application's standard output (the command line in this case). There's nothing too special happening here, we're using the traditional Ruby puts method to give us some feedback on the console.

Running this does start the simulator. To quit the simulator, you can use command + Q or from the console window, press Ctrl + D or Ctrl + C.

Since our phone doesn't really have a console to receive the standard output message, let's take this up a notch by adding a label to the application and configuring it to be a more traditional "Hello World" on the device. So, we plan on making a label that will have the text "Hello World", correct? Let's create the test for that.

We'll start by making a new file in our spec folder called hello_world_controller_spec.rb and we'll make it look like the following:

Step 4 – writing "Hello World" the RubyMotion way

Let's inspect the testing code from the previous image. Everything looks similar to the other code but, as you can see, there's no need to make a before block, since we are able to access the controller by simply stating the controller we're testing on the second line. This shortcut works for any ViewController you're testing!

The actual testing and use for the variable starts with our specification. We grab the label so we can apply our two requirements on the following lines. We check the text value, and verify that the label has been added to the view. This might seem quite nebulous without knowing what we're testing, but the logic is simple. We want to make sure there's a label that says "Hello World" and we want it to be visible. Running these tests will fail, which puts us on track to write the actual "Hello World" portion.

You should add the following code to your project's hello_world_controller.rb file:

Step 4 – writing "Hello World" the RubyMotion way

There it is! We've finally written a real Hello World application! Let's break this into each line and see how it was done. Line three is like our normal puts method to the console, but I've upgraded it to p, which is shorter to type, and actually calls the .inspect method on the parameter. This way you receive a human-readable representation of whatever object you've passed. This helps for outputting all kinds of objects as well as strings!

On line five, I get rid of the lifeless black background, and replace it with white. The view object you see referenced is the view of the controller. In the testing code, you see us reference this same view by controller.view, which points to the same object. On this view object, I'm setting the backgroundColor property (the available property on the UIView classes) to UIColor.

Properties of UIView can be found in the Apple Documentation (http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIView_Class/UIView/UIView.html).

We create a UILabel, which we intend to add to view. @label.text sets the text property, and next line sets the frame. Line 9 uses a RubyMotion shortcut for the frame. Normally, you would need to use Objective-C's CGRectMake with Cartesian coordinates as well as width and height, but instead you can pass in the array as we have it in the previous code example. The format is [[x coordinate, y coordinate],[width, height]]. Where the first array portion is the origin and the second is the size. We actually already dealt with a frame when we sent the bounds value to initWithFrame in the AppDelegate class. The last line of the method takes this object we've been working on and places it on view!

Step 4 – writing "Hello World" the RubyMotion way

Note

The coordinate system is called the UIKit Coordinate System. The top-left corner of your screen is x == 0 and y == 0. In this sense, the x coordinate is sometimes called the left value and the y is the top value (à la CSS). If you deal with Core Graphics, the coordinate origin is at the bottom-left corner.

The UIKit Coordinate System is often called a ULO Coordinate System (Upper-Left-Origin Coordinate System).

The Core Graphics Coordinate System is often referred to as LLO (Lower-Left-Origin Coordinate System).

Coordinate size per device: width x height

iPhone 4s: 320 x 640

iPhone 5: 320 x 568

All iPads: 768 x 1024

It's important to note that retina devices have the same coordinate system, but finer detail. One point does not necessarily correspond to one physical pixel.

Line 8 satisfies the first requirement of our specification test that the text matches "Hello World". Line 10 satisfies our second requirement, by verifying the label is added. Running specification tests with the preceding code yields a passing result! Actually running the application shows the following app:

Step 4 – writing "Hello World" the RubyMotion way

Yeah, it's nothing you'd show your friends, but it's definitely something to be proud of because it's a complete application for your iPhone!

Now we can refactor our code. At a simple glance we can tell that our "Hello World" text is anything but centered. Let's grab the position of the view and center ourselves neatly!

We'll add sweet symmetry to our app with the following code:

Step 4 – writing "Hello World" the RubyMotion way

Voila! We've quickly modified our original application to handle the frame dynamically.

We calculate the center coordinate along the horizontal (x) by grabbing our current main view frame width and dividing by 2. Likewise, we do the same for the vertical (y). On line 11, we call the UIView class's sizeToFit method. This sizes the frame around all contents and subviews, essentially making the label frame exactly the size of the text. Then, lastly, on line 12, we set the label's center to the calculated center from lines 6 and 7. This yields the aesthetic result of a completely horizontally and vertically centered label!

Step 4 – writing "Hello World" the RubyMotion way

Additionally, if you're keen enough, you may have realized the great ancillary benefit to this method. Your application will now center the text on any device, even an iPad!

Why not try it out? Let's make our app so it can render on an iPad. All we have to do is add device_family to our Rakefile:

Step 4 – writing "Hello World" the RubyMotion way

By the preceding line 8, we've added the ability of our app to go on the iPad. The order of the parameters in this array will adjust which device is default. To build our app specifically for the iPad we run the following:

$ rake device_family=ipad

And there you go! Your application is now an iPad application, and your label is still centered! You may need to clean and rebuild if your application is not switched to iPad. This can be done with a quick call to rake clean.

All the code of the Hello World project can be found at https://github.com/GantMan/HelloWorld or http://www.packtpub.com/support.

Congratulations on your first RubyMotion application! You deserve it!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset