Blog

Advanced Development Tricks For iOS Modules In Appcelerator Titanium

Posted on Thu, Mar 06, 2014 @ 01:10 PM

While we have already written quite a few articles about mobile app development using Appcelerator Titanium as well as some for module development, there are still a few things we can learn about. So without much further ado, here are a few little known tricks that you can use when creating your next module for mobile development.

Appcelerator Titanium Mobile App Development: Advanced Tips Module Creation 

Let's Start with Targets 

I wrote about targets before and as you will see next, they have become very handy when developing modules in Xcode. 

When following the tutorial on Appcelerator’s website, you will see that in order to create a module for your mobile app, you have to run the following command in a Terminal window: 

titanium.py create --platform=iphone --type=module —dir=. —name=test —id=com.example.test 

Note: the titanium.py command is something you need to set up yourself. I won’t go into this, but it’s very well documented on this link. 

This will create an Xcode project that allows you to write code for the module. 

However, if you want to test your module, you would need to write a javascript code in the app.js file which is included in the example folder inside the project that was generated, and then type the following command in a Terminal window: 

titanium.py run  

This will compile the project and launch the simulator so you can test your module. However, this can be very tedious as it can take a long time to compile and run the app. 

There is a better way to do this though. 

Go to your Xcode project and then select the top item on the left of the window:

 

0. Xcode project resized 600 

Select the Add Target… button and you will be greeted with the following window:

 

1. Add target for iOS resized 600

 

Select Single View Application and follow the instructions until your new target is created. You should have something like this:

 

2. single view application resized 600

 

I recommend you make a group (and a folder in the Finder) where you put all the common source files. These common source files should be added to both the test module and TestApp target. Like this:

 

3. Common source files xcode resized 600

 

Notice how the CommonView.m is part of both the test and TestApp target. 

So you can now work on your module and test it out on a native app before sending it over to Appcelerator Titanium. Much more efficient and faster as far as development goes. 

Callbacks instead of events 

When some part in your module takes a long time to process, you may be tempted to fire an event when that operation has completed. 

In your module you would write something like this:

-(id)getUsersFromURL:(id)args
{
    dispatch_queue_t queue = dispatch_queue_create("lengthy_queue", NULL);
   
    dispatch_async(queue, ^{
       
        [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://somewebservice.com/users"]]
                                           queue:[NSOperationQueue currentQueue]
                               completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
                                   // do something here
                                  
                                   dispatch_async(dispatch_get_main_queue(), ^{
                                       [self fireEvent:@"myEvent" withObject:@[@"Hello World"]];
                                   });
                               }];
    });
   
    dispatch_release(queue);
   
            return NUMBOOL(YES);
}

 

Note: This code is for example purposes only, do not copy and paste it and expect it to work! 

As you can see, once the NSURLConnection has finished, we fire an event on the main queue which is then caught by JavaScript by writing something like this:

myModule.addEventListener('myEvent',function(e){
  alert(“Cool”);
});
 
myModule.getUsersFromURL();

 

Pretty cool, right? However, with JavaScript you are able to add a function as an argument, which we call callbacks. 

To add support for callbacks to your module you would need to adapt the previous code to something like this:

-(id)getUsersFromURL:(id)args
{
    id _out = nil;
    ENSURE_ARG_AT_INDEX(_out, args, 0, KrollCallback); // 1
   
    KrollCallback *callback = (KrollCallback*)args[0]; // 2
   
    dispatch_queue_t queue = dispatch_queue_create("lengthy_queue", NULL);
   
    dispatch_async(queue, ^{
       
        [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://somewebservice.com/users"]]
                                           queue:[NSOperationQueue currentQueue]
                               completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
                                   // do something here
                                  
                                   dispatch_async(dispatch_get_main_queue(), ^{
                                       [callback call:@[@"Hello World"] thisObject:self]; //3
                                   });
                               }];
    });
   
    dispatch_release(queue);
   
            return NUMBOOL(YES);
}

 

-    In 1, we ensure that the argument that is being sent is of type KrollCallback, i.e. a callback. If it isn’t, the module will crash 

-    In 2, we setup a variable of type KrollCallback, which is stored in the first element of the args argument 

-    In 3, we call the callback and send it the string “Hello World” 

Now in JavaScript, you implement this like so:

myModule.getUsersFromURL(function(message) {
            alert("Message is: "+message);
});

If it is done correctly, after running this code you would see an alert with the following message: Message is: Hello World 

Great! Isn't it? 

Using Xib files 

This is a feature that’s not well documented and I had to figure out myself the hard way. 

It is perfectly possible to use xibs in a module, but there are some caveats. 

First of all, you need to get your path right. You can’t just load a nib the usual way. For instance, when you are loading a view controller, you would normally write code like this:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
               self = [super initWithNibName:”MyViewController” bundle:nil];
 
            // the rest of the init code
}

 

This won’t work, the init method from super will look at the bundle from the app the module is running on, which does not contain the MyViewController.xib file. 

To fix this, you need to actually pass the correct bundle to the init method. 

To get the correct bundle you would need to write code like this:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
            NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"modules/com.itexico.test/"];
        NSBundle *bundle = [NSBundle bundleWithPath:path];
    self = [super initWithNibName:”MyViewController” bundle:bundle];
            // the rest of the initialization
}

 

The only thing you need to keep in mind here is that the last component of the path is most likely different for your module, so you would need to adapt it. 

Now, to load the xib files you would need to convert them to nibs first. To do this you would need to use a neat command line tool called ibtool. 

The following code snippet is for a ruby script that takes a list of xib files and converts them to nibs.

#!/usr/bin/ruby
ARGV.each do |value|
system "ibtool \"#{value}\" --compile \"#{value.gsub(/\.xib$/, ".nib")}\""
end

 

 

The result of this script is a bunch of nib files that you can place in the assets folder of the module folder. 

Using ARC 

Finally, let’s talk about Automatic Reference Count and modules. Unfortunately, a module for Appcelerator Titanium does not support ARC, but if you write custom code, you can still use ARC. 

Let me explain. 

First, in your project, for the module target, enable ARC like this:

 

4. ARC resized 600

 

Then, for each file that start with ComExampleText (or your own bundle id which you set up when creating the module), disable arc by adding the -fno-objc-arc compiler flag to just those files like this:

 

5. test resized 600

 

If you look closely, the CustomView does not have the compiler flag, but the other files do. 

Only add the compiler flag to files that are subclasses of a Ti<Object>, like TiModule, TiView, TiProxy, etc., etc… 

Final Thoughts 

I’m sure there are many more advanced tricks to native iOS module development for mobile apps, but with the previous tricks I think your knowledge of making modules will reach a higher level. 

I hope you found these tricks useful and should you have any questions or comments you can add them below this post. 

Have fun!

About the Author 

Jesus De Meyer has a Bachelor's degree in Multimedia and Communication Technology from the PIH University in Kortrijk, Belgium.  He has 15 years of experience developing software applications for Mac and more than 5 years in iOS mobile app development. In his spare time he works on his own mobile apps. 

Contact Us

Tags: Tutorial, Mobile App Development, Titanium, iOS