Titanium Module Development for iOS – Getting Started

Recently I had the “pleasure” of working with Titanium from Appcelerator. I needed to make a module for an iOS and Android library of a customer, so that developers could use the library from Titanium.

Basically, I followed this guide, but it showed to be incomplete or missing some information. This post is a follow-up on appcelerators own guide, which documents some of the gotchas and missing information, that I found out along the way.

I did all this on a Mac.

Step 1: Install Titanium Studio

Easy peasy (lemon squeezy). Just do as they say. I installed v1.8.1.

Step 2: Install SDKs

Then fire up the newly installed studio tool (aka: Eclipse), and have it install the mobile SDKs.

Step 3: Do the titanium alias thing

The guide mentions how to setup an alias, that runs the titanium.py script. That’s good advice.

One caveat here is: If you have something on the PATH variable which is also named titanium (as the alias also is named), Titanium Studio will try and execute that, instead of the alias. I had a directory named titanium on the PATH, and that’s not really that much of an executable.

Step 4: titanium create…

When the guide tells you to use titanium create... to create an initial module project, you should remember to avoid “-” (dash) in project name.

After the “create” command, you will now have an xcode project. Open up this project in xcode, open “Build settings” for the project, to be able to set TITANIUM_SDK variable. You can set it on 3 levels:

  • project level: ~/Library/Application Support/Titanium/mobilesdk/osx/$(TITANIUM_SDK_VERSION)
  • in target <yourmodulelib>: ~/Library/Application Support/Titanium/mobilesdk/osx/$(TITANIUM_SDK_VERSION)
  • in target “Build & Test”: $HOME/Library/Application Support/Titanium/mobilesdk/osx/$(TITANIUM_SDK_VERSION)

Note the use of $HOME in “Build & Test” target. This is because that target executes a python command in the shell, and it won’t expand tilde to homedir.

Check the binary build

The appcelerator guide mentions how to build.

BUT: It outputs a quadrillion lines when building, and when steps fail to build in the process, it will happily continue building, with the final result, that stuff is missing in the output artifact. And because of the humongous amount of output, you wont notice.

The appcelerator guide mention that you need markdown2 for documentation generation to work, and that bad things will happen if not. However, as mentioned, it wont stop from building, and the error will drown in all the other output from build. At first, you wont see any change, but the zip packaging will fail if you (like me) are missing both markdown2 and markdown.

Somewhere in all the build output, you will see:

DEBUG] Traceback (most recent call last):
[DEBUG] File "./build.py", line 216, in <module>
[DEBUG] package_module(manifest,mf,config)
[DEBUG] File "./build.py", line 193, in package_module
[DEBUG] docs = generate_doc(config)
[DEBUG] File "./build.py", line 66, in generate_doc
[DEBUG] import markdown
[DEBUG] ImportError: No module named markdown

which is due to this code in build.py failing:

        try:
                import markdown2 as markdown
        except ImportError:
                import markdown

it nicely catches if I don’t have markdown2, but has no catch if I don’t have markdown. Maybe I am the one in a million, that don’t even have markdown :-)

Also check, that the zip contains LICENSE and build.xcconfig, as that are some of the last files added in build process.

Other Gotchas

xcodebuild fail

When doing titanium run, it will continue running app, even though xcodebuild failed. Again, you might not notice due to all the build output.

frameSizeChanged

When developing module view classes, you are told to implement frameSizeChanged to adjust your view to its new bounds. The example also shows how to lazily construct your view, the first time frameSizeChanged is called. That is fine, just remember that frameSizeChanged never gets called, until you actually put some dimension on the view.

So, if you have this code in the titanium app that uses your module to create a view:

var view = yourlib.createYourView({
   "foo":42, 
   "bar":666
});

frameSizeChanged never will get called, which is turn means you won’t construct your view, which results in view proxies not forwarding messages to the view (because no view has been set).

If you change it to:

var view = yourlib.createYourView({
   "width":320,
   "height":480-20-44+1, // WTF: What's up with the one pixel off?
   "foo":42, 
   "bar":666
});

frameSizeChanged will be called.

Logging

To log output from objc code to titanium console, use: TiLogMessage(@"blah");

Xcode 4.3

Don’t upgrade yet (or be sure to keep the 4.2 version when upgrading) because it is not supported yet. Will change soon, I guess.

Javascript only modules

If you are developing javascript-only modules, you should correct TITANIUM_SDK in titanium.xcconfig, or else from compiler import Compiler will fail when building. (see TIMOB-7001).

And while we are at it. You cannot have both native (iOS or Android) AND javascript module code in same module.

I wanted to create a javascript part of my module, to provide a javascript enum-like thing, to support an objc enum type in the module API. So, I created an <mymoduleid>.js file and added my js code there. After compiling, it didn’t work. Hey, I thought, I will just get rid of the js module code and continue with my iOS native code. But then that code failed too. It was as if the js part was still there.

After a lot of cleaning in module project, module install places, build paths etc., I found out, that when doing javascript module development, that javascript file actually gets compiled into Classes/<yourmoduleid&gtModuleAssets.m::moduleAsset as a HEX string. Building actually goes in and modifies the source. Yikes! Cleaning that HEX string made my iOS module code come back to life.

Checking types and throwing exceptions

The module APIs are not type safe at all (not that surprising), so first thing you need to do in your module code, is checking types. If types fail to meet the needs, you need to report it somehow. This can be done with some exception throwing, like this:

  [self throwException:TiExceptionInvalidType 
             subreason:[NSString stringWithFormat:@"expected: %@, was: %@", [NSArray class], [args class]] 
              location:CODELOCATION];

Here I’m checking for NSArray type.

March 10, 2012 В· polesen В· 2 Comments
Tags: , ,  В· Posted in: Programming

2 Responses

  1. Art - May 11, 2012

    Nice post! Xcode 4.3 is now supported by Ti 2.x
    Any tips on how to debug an iOS module for Ti? I’ve been debugging mine in Xcode, but I’m not seeing variable values in the Xcode debugger for my module.
    Wondering how others are debugging their Ti modules.

  2. polesen - May 11, 2012

    Sorry, no tips on debugging ios ti modules, …except from following the path of introducing no bugs in the first place:)