iPhone Module Development Guide

This is work in progress and we're in the process of finalizing the module API. Feedback welcome.

iPhone Design

The iPhone Module Framework is based on an Objective-C protocol, named TitaniumModule. TitaniumModule defines a simple set of lifecycle methods that are used by the main Titanium system.

Titanium provides a base implementation class that implements the TitaniumModule protocol and provides a number of useful utility methods, named TitaniumBasicModule. You should extend this module if you intend to build your own module in Titanium. Hereafter, we'll use Module to refer to an implementation that extends TitaniumBasicModule.

A Module has 2 primary constituent parts that are required to extend Titanium:

  1. JavaScript API
  2. Module implementation

The JavaScript API is part of the Module, but is accessed by developers using JavaScript to access Module features and capabilities. In some cases, a Module may not have any direct API exposure to the developer.

Modules references are detected by the Titanium compiler at build time to determine if and when they should be loaded when the application is running. All Modules live in the Titanium JavaScript namespace. When naming a Module, care must be taken not to use a Module name of an existing Titanium built-in module. Your Module may not load or may have unpredictable results if you use the same name of an existing Titanium module.

At runtime, when a Module is needed, the Module will be instantiated and its startModule method will be invoked. Modules are singletons and are only loaded once. When the Module is being unloaded, its endModule method will be invoked. Generally, you should not overload these methods. If you do overload these methods, please ensure that you call the superclass method.

Modules should overload the configure method to do configuration and setup of the Module. This method should bind any appropriate Module APIs and ready the Module for invocations.

Module Files

You can create two types of modules:

  • Project Module. These are modules that are included in your application project and build alongside your application code when the Titanium compiler builds your application.
  • Redistributable Module. These are standalone modules that are built into static libraries that can be used by multiple applications and not specifically tied to an application project.

Currently, this tutorial focuses on Project Modules as they are the simplest types of modules to get started with. Titanium itself uses Redistributable Modules.

To create a Project Module, you'll create a directory under you root project folder : <ROOT>/modules/iphone.

All your Module code (header files, implementation files, NIBs, graphics, etc) are included in this directory (or subdirectories).

Your First iPhone Module

After creating your Module directory, create a file named FirstModule.m. In this file, paste the following code:

#import <TitaniumBasicModule.h>

@interface FirstModule : TitaniumBasicModule
{
}
@end

@implementation FirstModule

-(NSString*) hello: (NSString*) message
{
   return [NSString stringWithFormat:@"You said: %@", message];
}

-(void) configure
{
   [self bindFunction:@"hello" method:@selector(hello:)];
}
@end

Now, in your Javascript code, call the following method:

Titanium.API.info(Titanium.First.hello("world"));

When you run your application, your Module will be compiled and you should see the INFO level logging in your Titanium Developer console: You said: world

Module Naming

Your module is automatically named by convention. Your module will be automatically bound to the name of your class name, excluding the Module. If you have a mixed case Module name or would like to bind your Module to a different JavaScript namespace, you can control the name by overriding the method:

-(NSString*)moduleName
{
   return @"FooBar";
}

Parameters

Modules parameters are return type are generally boxed and unboxed for you by Titanium. Parameters passed from JavaScript to native code must be JSON serializable, meaning they cannot reference functions or other non-serializable references. JavaScript objects are passed as an NSDictionary. Strings are passed as NSString and numbers are passed as NSNumber instances. You can also declare a parameter of type id if you're not sure the conversion and then check the class type through a simple test.

For example, to pass a JavaScript object to a method you might define:

-(void)foo:(NSDictionary*)params
{
   NSNumber *x = [params objectForKey:@"x"];
   NSNumber *y = [params objectForKey:@"y"];
}

And then in JavaScript you could invoke as:

Titanium.First.foo({"x":1, "y":2.0});

Module Files

Any files that are non-source files (i.e., not .h, .c, .cpp, .m, .mm, .a) will be copied into your application bundle and are available in your application at the root. In JavaScript or HTML, you can simply reference them relative as if they were in the same directory as your root files.

Module Events

Modules can register and receive Module events from Titanium. Modules can also send events which can be received by other Modules. In general, Modules will consume events.

To consume a Module event, you implement one or more event methods. You must also override the following method to cause the Module to receive events:

- (BOOL) wantsNotifications
{
   return YES;
}

By default, TitaniumBasicModule will return NO as an optimization.

Titanium can send many events to the Module. However, you only implement the event methods you'd like to actually handle.

A simple event is called eventNativeControlEvent which is invoked any time a native Titanium control sends an event to JavaScript -- for example, a focus change event.

You could get notified of this event with the following example code:

-(void)eventNativeControlEvent:(id)source properties:(NSDictionary*)properties
{
  NSLog(@"the event was: %@ fired from: %@", [properties objectForKey:@"event"], source);
}

This will simply log all events and their source to the console.

For event methods, the signature is always the same. The source will be the source that triggered the event and the properties will be an optional dictionary of properties -- or nil if no properties are part of the event.

All event methods will be called on the main UI Thread. Care must be taken to not perform unnecessary background work on this thread.

Advanced Examples

The next step is to look at the code to more advanced module examples.

  • OpenGL demo: demo that shows how to create an OpenGL based module that combines a native OpenGL drawing surface with a composite view in Titanium.
  • Camera Overlay demo: demo that shows how to add an image overlay on top of a camera view. This demo requires iPhone SDK 3.1+.
Last Modified on October 26, 2009, 09:10 AM by Daniele Maiorana Edit | History