Chapter 11. Extending RubyMotion

The DRY principle states the following:

"Every piece of knowledge must have a single, unambiguous, authoritative representation within a system."

We are now approaching the end of this book. So far we have learned how to quickly make iOS applications with RubyMotion. To make this process even more rapid, RubyMotion lets us use special RubyMotion-flavored gems and wrappers. Gems and wrappers are Ruby programs that are wrapped into a self-contained format. These are generally open source projects, which other developers can use in their applications or can even contribute back to these projects. Fortunately, RubyMotion has a very enthusiastic community; within months of launching RubyMotion's tool chain, plenty of gems were introduced that implement many laborious tasks fairly quickly. In this chapter, we will learn how to augment our application by using RubyMotion-flavored gems. The following topics will be covered in this chapter:

  • RubyMotion gems
  • CocoaPods

RubyMotion gems

Use of gems is based on the programming practice of Don't Repeat Yourself (DRY), which states that when some piece of code is ready to use and is available, why bother working on it again. The RubyMotion community may be very young right now, but it already has some amazing gems that make a lot of tiring tasks pretty easy. Some gems even target challenging functionalities in a very simple manner.

In this chapter we will cover the following RubyMotion-flavored gems:

  • Teacup
  • BubbleWrap
  • motion-addressbook

Teacup – say goodbye to Xcode and XIB files!

Designing a UI for iOS apps is a tough job, especially for developers who have worked previously on easy-to-learn-and-implement web technologies. Teacup is a gem that will make your life really easy. Teacup augments your ability to quickly design and style the views of your RubyMotion application; you can easily create layouts while keeping your code DRY.

Let's create an application and learn how easy it is to use Teacup:

$motion create TeaCupMotion

We will be using Bundler (which is also a Ruby gem) to install all our gems. Bundler also helps us manage application dependencies, so that the exact version of the gems used are available for the application to run.

Tip

Bundler comes as a default dependency manager for popular frameworks such as Ruby on Rails.

Let's add Bundler to our application:

  1. Update the Rakefile with the following lines of code:
    $:.unshift("/Library/RubyMotion/lib")
    require 'motion/project'
    require 'bundler'
    Bundler.require
    
  2. With Bundler, we require a Gemfile in which we can mention details about the gems we will use with our application. Next, let's create a Gemfile and add the following lines of code in it:
    source "https://rubygems.org"
    gem "teacup"
    

    So, in the future, if you want to add any new gem to your project, you can simply add it to this file.

  3. Next, let's run bundle install and we're good to go:
    bundle install
    

    Note

    The bundle install command adds a Gemfile.lock file to your repository. This ensures that other developers on your app, as well as your deployment environment, will all use the same third-party code that you are using now.

  4. Next, update the app_delegate.rb file with the following code:
    class AppDelegate
      def application(application, didFinishLaunchingWithOptions:launchOptions)
        @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
        myNavController = RootController.alloc.init
    
        @window.rootViewController = UINavigationController.alloc.initWithRootViewController(myNavController)
        @window.rootViewController.wantsFullScreenLayout = true
        @window.makeKeyAndVisible
        true
      end
    end

In this code, we are only initializing an instance of RootController, just like we do with every application. As you may remember, the controller is where all our application logic resides.

So far, in various chapters we have made RubyMotion iOS applications in a traditional way. Let's use Teacup in our application this time, and add styles by making use of its Cascading Style Sheets (CSS) type syntax.

Let's create a directory named style and add a new file with the name of style.rb in it. Add the following code to the style.rb file in the style folder:

Teacup::Stylesheet.new(:style) do

  style :your_layout,
    landscape: true

  style UILabel,
    textColor: UIColor.blueColor
  style :label,
    text: 'Awesome',
    backgroundColor: UIColor.whiteColor,
    top: 10,
    left: 100,
    width: 100,
    height: 20

end

Let's understand the preceding code:

  1. First, we have created a stylesheet named style.
    Teacup::Stylesheet.new(:style) do
    …
    end

    This convention is provided by Teacup to create a new stylesheet.

  2. Next, we have created a specific layout for your views, using CSS-based syntax.
      style :your_layout,
        landscape: true

    This will create a style named your_layout and will enable the landscape rotation (otherwise, only portrait orientation is enabled).

  3. Next, we have added style for all UILabel instances.
      style UILabel,
        textColor: UIColor.blueColor

    The preceding line of code gives text color to all UILabel instances that are defined inside the style. Since we apply a style to all the labels when using UILabel, if we want to style a specific element, we have to add the following commands:

    style :label,
      text: 'Awesome',
      backgroundColor: UIColor.whiteColor,
      top: 10,
      left: 100,
      width: 100,
      height: 20

Here, label is like a class. This will do the styling for the label.

To understand this better, let's create a view. Perform the following steps to create a view:

  1. Create a file named root_view_controller.rb and add the following code to it:
    class RootController < UIViewController
    
      stylesheet :style
    
      layout :your_layout do
        @label1 = subview(UILabel, :label)
     
      end
    
      def shouldAutorotateToInterfaceOrientation(orientation)
        autorotateToOrientation(orientation)
      end
      
    end

    As we have created a new controller file, we must make the corresponding changes to the app_delegate.rb file. Make these changes in your app_delegate.rb file as shown in the previous chapters.

    In the preceding code snippet, first we have given the stylesheet a name, which is done using stylesheet:style, and then we have specified a layout named your_layout and passed label : @label1 = subview(UILabel, :label) to it.

  2. Let's fire up the terminal and test our application.
    $rake
    

    The following is the output:

    Teacup – say goodbye to Xcode and XIB files!

    We can see the text Awesome appear on the simulator screen and it is styled as we have defined in the stylesheet.

    Tip

    Teacup implements the viewDidLoad method and instantiates any views. If you want to implement your own viewDidLoad method, make sure to call super.

    We can also define different stylesheets for changing dimensions as we rotate the device, such as the landscape and portrait modes. Let's try this in our next example.

  3. Now update the stylesheet, that is, the style.rb file, with the following code:
        style :label,
        text: 'Awesome',
        backgroundColor: UIColor.whiteColor,
        top: 10,
        left: 100,
        width: 100,
        height: 20,
        landscape: {
            backgroundColor: UIColor.redColor,
         }
    
  4. Run the application and rotate the screen from the simulator menu by navigating to Hardware | Rotate Left. You will see that as the screen rotates the background color of the label changes.
    Teacup – say goodbye to Xcode and XIB files!
  5. Now, let's do a few more things in the same example. Add the following code in the style.rb file:
      style UITextField,                # Defining styles based on view
    class instead
      textColor: UIColor.redColor
    
      style :field,
      left:   10,
      top:    10,
      width:  200,
      height: 30,
      landscape: {
        width: 360  # make it wide in landscape view
      }
    
      style :search, extends: :field,
      backgroundColor: UIColor.whiteColor,
      left: 20,
      top: 70,
      placeholder: 'Search Box'     
      style :search_new, extends: :field,
      backgroundColor: UIColor.redColor,
      left: 20,
      top: 110,
      placeholder: 'Search Box'

    Here we have created two text field boxes.

  6. Now, update the root_controller.rb file.
      layout :your_layout do
        @label1 = subview(UILabel, :label)
        @search = subview(UITextField, :search)
        @one_more_search = subview(UITextField, :search_new)
     
      end
  7. Let's test our application in the simulator.
    $rake
    

    The following is the output:

    Teacup – say goodbye to Xcode and XIB files!

With the preceding example, we can see how easy it is to design views with the Teacup gem; it has delivered a way to create interfaces programmatically with ease. We have shared a few of the features of this amazing gem; you can explore more at https://github.com/rubymotion/teacup.

BubbleWrap – making Cocoa APIs more Ruby-like

BubbleWrap is a collection of very well-tested helpers and wrappers used to wrap Cocoa SDK code and provide more Ruby-like APIs for RubyMotion. It provides wrappers for a lot of iOS Cocoa SDK code, such as camera, notification center, HTTP, and many more.

We can do a lot of things very easily. For example, to perform a GET HTTP request with BubbleWrap, we require the following simple code snippet:

BW::HTTP.get("https://twitter.com/rubymotion") do |response|
  p response.body.to_str
end

In Chapter 6, Device Capability – Power Unleashed, we have learned about device capabilities—implementing camera functionalities in your app. We have written quite a lot of code there, but with BubbleWrap things get really simplified. We only require the following code snippet for using a camera in our application:

BW::Device.camera.front.picture(media_types: [:movie, :image]) do |result|
  image_view = UIImageView.alloc.initWithImage(result[:original_image])
end

BubbleWrap also provides a module named App that can be used while running the application. To understand this, perform the following steps:

  1. First, create a small sample application.
    $motion create UseBubbleWrap
    
  2. Update the Rakefile to include a Bundler that will help us install the BubbleWrap gem easily.
    require 'bundler'
    Bundler.require
  3. As shown in the last section, let's add a Gemfile to our project with the following code:
    source :rubygems
    gem 'bubble-wrap'
  4. Run the following command to install the BubbleWrap gem:
    $bundle install
    
  5. Next, let's fire up the terminal to test the App module on the console:
    $rake
    
  6. To use the App module, run the following commands in REPL:
    (main)> App.name
    => "UseBubbleWrap"
    
    (main)> App.identifier
    => "com.yourcompany.UseBubbleWrap"
    
    (main)> App.documents_path
    => "/Users/abhishek/Library/Application Support/iPhone Simulator/6.1/Applications/3CF89A96-F390-4A7D-89B8-2F0E7B54A38A/Documents"
    
    (main)> App.resources_path
    => "/Users/abhishek/Library/Application Support/iPhone Simulator/6.1/Applications/3CF89A96-F390-4A7D-89B8-2F0E7B54A38A/UseBubbleWrap.app"
    
    (main)> App.frame
    => #<CGRect origin=#<CGPoint x=0.0 y=20.0> size=#<CGSize width=320.0 height=460.0>>
    (main)> App.states
    => {}
    
    (main)> App.shared
    => #<UIApplication:0x9530920>
    
    (main)> App.current_locale
    => #<__NSCFLocale:0x966a040>
    (main)> App.alert("This is nice!!")
    => #<UIAlertView:0xa8433f0>
    
    (main)>  App.run_after(0.5) {  p "It's #{Time.now}"   }
    => #<__NSCFTimer:0x93760c0>
    (main)> "It's 2013-05-10 18:47:34 +0530"
    
  7. There is another module named Device that provides many options related to the current device. Let's once again fire up REPL in our terminal and execute the following commands:
    $rake
    (main)> Device.iphone?
    => true
    
    (main)> Device.ipad?                 
    => false
    
    (main)> Device.front_camera?
    "This method (front_camera?) is DEPRECATED. Transition to using Device.camera.front?"
    => false
    
    (main)>  Device.screen.width
    => 320.0
    
    (main)> Device.screen.height
    => 480.0
    
    (main)> Device.orientation
    => :portrait
    

There are tons of helpers that come with the BubbleWrap gem. It will be helpful for your project if you have a look at the BubbleWrap documentation at http://bubblewrap.io/.

motion-addressbook – access phonebook easily

In Chapter 6, Device Capability – Power Unleashed, we had discussed in detail how to use the Address Book technology for iOS devices. In this section, we will use a special gem for RubyMotion named motion-addressbook that simplifies using the Address Book.

We will perform the following actions in this section:

  • Create a sample app with the motion-addressbook gem
  • Pull the data from the device's Address Book
  • Display it on the screen

Let's start by performing the following steps:

  1. Create a sample application.
    $motion create AddressBook_example
    
  2. Next, let's include the motion-addressbook gem in the Gemfile.
    source :rubygems
    gem 'bubble-wrap'
    gem 'motion-addressbook'
  3. Bundle install from the command line to include this gem in our project:
    $bundle install
    
  4. Let's create a file named addressbook_controller.rb in which we will add a button and three labels. With the button, we will access our address book and choose the desired contact. In the labels, we will display the data of the user, which we have copied from the address book. Add the following code in your addressbook_controller.rb file:
    def viewDidLoad
        view.backgroundColor = UIColor.underPageBackgroundColor
        load_button
        load_labels
      end
    
      def load_button
    
        @phonebook_button = UIButton.buttonWithType(UIButtonTypeRoundedRect)
        @phonebook_button.frame = [[50, 20], [200, 50]]
        @phonebook_button.setTitle("Click from Contacts", forState:UIControlStateNormal)
        @phonebook_button.addTarget(self, action: :addressbook_access, forControlEvents:UIControlEventTouchUpInside)
        view.addSubview(@phonebook_button)
    
      end
    
    
      def load_labels
    
        @first_name = UILabel.new
        @first_name.text = 'First Name'
        @first_name.frame = [[100,100],[150,50]]
    
        @last_name = UILabel.new
        @last_name.text = 'Last Name'
        @last_name.frame = [[100,160],[150,50]]
    
        @organization = UILabel.new
        @organization.text = 'Organization'
        @organization.frame = [[100,220],[150,50]]
    
        view.addSubview(@first_name)
        view.addSubview(@last_name)
        view.addSubview(@organization)
      end
  5. Add the following code in the app_delegate.rb file so that our delegate points to our address book controller:
    class AppDelegate
      def application(application, didFinishLaunchingWithOptions:launchOptions)
    
      @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
      @window.rootViewController = AddressbookController.alloc.init
      @window.makeKeyAndVisible
    
      
        true
      end
    end
  6. Let's fire up the terminal and run our app in a simulator to check if we are able to see our three labels and a button.
    $rake
    

    The following is the output:

    motion-addressbook – access phonebook easily
  7. In the preceding code snippet, we have mentioned a method named addressbook_access. To access the Address Book, we need to use the AddressBook picker that lets us open the device's Address Book in our application and pick data from it. With this method, we will be doing the same. Let's create this method in our addressbook_controller.rb file and add the following code to it:
      def addressbook_access
    
        AddressBook.pick { |person|
          if person
             first_name = person.attributes[:first_name]
             last_name = person.attributes[:last_name]
             org = person.attributes[:organization]
             @first_name.text = first_name
             @last_name.text = last_name
             @organization.text = org
    
          else
            # write some cancel code
          end
        }
    
      end
  8. Let's fire up the terminal and run our app in a simulator to check if we are able to access the Address Book and import the desired contact details in our application.
    $rake
    

    The following is the output:

    motion-addressbook – access phonebook easily
  9. Once we select any contact, we will get its details on our application, as shown in the following screenshot:
    motion-addressbook – access phonebook easily

That's it, we are done. It's the same application we had created in Chapter 6, Device Capability – Power Unleashed, but with motion-addressbook, we have substantially less code.

Let's understand what we have done here. The motion-addressbook gem gives us many options to easily use the device's Address Book. In the addressbook_access method, we have used the AddressBook picker by using AddressBook.pick, which opens up the device's Address Book for us. Once we select any contact, we get a person object that has a hash of all the attributes of the selected contact.

In our example, we have used the first_name, last_name, and organization values from the selected person object. However, the motion-addressbook gem has many more options that make working with the Address Book framework faster and easier. A few of them are as follows:

  • To create a new contact.
    AddressBook::Person.new
    #<AddressBook::Person:0xc360bc0 @address_book=nil @ab_person=nil @attributes={}>
  • To pull all the records from the address book.
    AddressBook::Person.all
    [#<AddressBook::Person:0x9d78c80 @address_book=#<__NSCFType:0xc0db6d0> @ab_person=#<__NSCFType:0x9d77ea0> @attributes={:first_name=>"Abhishek", :last_name=>"Nalwaya", :organization=>"Fun Inc."}>, #<AddressBook::Person:0x78f0a20 @address_book=#<__NSCFType:0xc0db6d0> @ab_person=#<__NSCFType:0x9d78520> @attributes={:first_name=>"Akshat", :last_name=>"Paul", :organization=>"PacktPub"}>, #<AddressBook::Person:0x78a5eb0 @address_book=#<__NSCFType:0xc0db6d0> @ab_person=#<__NSCFType:0x9d788b0> @attributes={:first_name=>"Laurent", :last_name=>"Sansonetti", :organization=>"HipByte"}>, #<AddressBook::Person:0x78c06e0 @address_book=#<__NSCFType:0xc0db6d0> @ab_person=#<__NSCFType:0x9d78700> @attributes={:first_name=>"Manu", :last_name=>"Singhal", :organization=>"Ruby Inc"}>]
  • To get a list of records based on a specific attribute.
    AddressBook::Person.find_all_by_organization('HipByte')
  • To get a list of records based on many conditions.
    AddressBook::Person.where(:email => '[email protected]', :organization => 'Fun Inc')
  • To create a new contact.
    AddressBook::Person.create(:first_name => 'Shi', :last_name => 'Foo', :email => [email protected]')
..................Content has been hidden....................

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