Extending Appcelerator Titanium Mobile through Native Modules

21,450 views

Published on

Presentation used for the speech I gave at the WHYMCA 2011 - Italian Mobile Developer Conference, held in Milan (IT) on may 20th 2011

Published in: Technology
3 Comments
21 Likes
Statistics
Notes
  • Hi, I haven't been notified about these comments, so sorry for replying only now...
    Wrt the project setup, I also tried your solution, though with more complex applications, from time to time you need Titanium to recreate the main project, so you end up re-creating references, etc. Now I use a command line script that basically performs the same operations (i.e. build&install the module and launch the main project).
    Wrt documentation & best practices: as you point out docs are pretty much inexistent and unfortunately the best 'documentation' I've found is the Ti SDK source code. Digging through the code can be quite time consuming, but it gives you also many hints on how things should be done. I don't know if what you find in this presentation can be considered 'best practice'. Actually this is 'my' way to develop titanium modules.
    Recently I started blogging at http://titaniumninja.com where I report my experiences with titanium in general, and with module development and SDK hacking in particular.
    Thanks for the comments,

    Olivier
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Also, is this best practice documented anywhere besides your slides? I've had a hard time finding helpful information beyond what's listed in the Module Developer Guide. Thanks.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Olivier, thanks for posting this. I'm curious about the test app configuration shown on slides 55 & 56. I'm trying to set my test project up so that I can modify Objective-C code in my module, then hit Build & Run on my test project, and have it automatically compile and link against my latest module changes. To make this work, I added a 'Build & Install' target to my module project, which is dependent on my main module target. The 'Build & Install' target copies & unzips my module into /Library/Application Support/Titanium. Then I added 'Build & Install' as a dependency on all my test app targets. Does this make sense, and do you have a better solution for this?
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total views
21,450
On SlideShare
0
From Embeds
0
Number of Embeds
957
Actions
Shares
0
Downloads
302
Comments
3
Likes
21
Embeds 0
No embeds

No notes for slide

Extending Appcelerator Titanium Mobile through Native Modules

  1. 1. ExtendingTitanium Mobile through Native Modules<br />Olivier Morandi<br />
  2. 2. # whoami<br />Olivier Morandi<br />Freelance mobile developer & software engineer<br />olivier.morandi@gmail.com<br />@olivier_morandi<br />https://github.com/omorandi<br />
  3. 3. Titanium Mobile<br />A set oftoolsfordevelopingcross-platform mobile applications in JavaScript<br />iOS (iPhone/iPod/iPad) <br />Android<br />Blackberry(through a commercial subscription)<br />http://www.appcelerator.com<br />https://github.com/appcelerator/titanium_mobile<br />
  4. 4. Anatomyof a project<br />Project directory<br />build/<br />android/<br />iphone/<br />Resources/<br />app.js<br />manifest<br />tiapp.xml<br />Buildfolders, per platform<br />Resourcefiles: JS code, images, sounds, SqliteDBs, etc.<br />Project files<br />
  5. 5. manifest<br />#appname: whymca<br />#publisher: olivier<br />#url: http://www.whymca.org<br />#image: appicon.png<br />#appid: com.whymca.test<br />#desc: undefined<br />#type: mobile<br />#guid: 746e9cb4-49f6-4afe-af0b-5de9f0116f65<br />
  6. 6. tiapp.xml<br /><?xml version="1.0" encoding="UTF-8" standalone="no"?><br /><ti:appxmlns:ti="http://ti.appcelerator.org"><br /> <deployment-targets><br /> <target device="iphone">true</target><br /> <target device="ipad">false</target><br /> <target device="android">true</target><br /> </deployment-targets><br /> <id>com.whymca.test</id><br /> <name>whymca</name><br /> <version>1.0</version><br /> <publisher>olivier</publisher><br /> <url>http://www.whymca.org</url><br /> <description>notspecified</description><br /> <copyright>2011 byolivier</copyright><br /> <icon>appicon.png</icon><br /> <persistent-wifi>false</persistent-wifi><br /> <prerendered-icon>false</prerendered-icon><br /> <statusbar-style>default</statusbar-style><br /> <statusbar-hidden>false</statusbar-hidden><br /> <fullscreen>false</fullscreen><br /> <navbar-hidden>false</navbar-hidden><br /> <analytics>false</analytics><br /> <guid>746e9cb4-49f6-4afe-af0b-5de9f0116f65</guid><br /><iphone><br /><orientationsdevice="iphone"><br /><orientation>Ti.UI.PORTRAIT</orientation><br /></orientations><br /><orientationsdevice="ipad"><br /><orientation>Ti.UI.PORTRAIT</orientation><br /><orientation>Ti.UI.UPSIDE_PORTRAIT</orientation><br /><orientation>Ti.UI.LANDSCAPE_LEFT</orientation><br /><orientation>Ti.UI.LANDSCAPE_RIGHT</orientation><br /></orientations><br /></iphone><br /><androidxmlns:android="http://schemas.android.com/apk/res/android"><br /></android><br /><modules><br /></modules><br /></ti:app><br />
  7. 7. app.js (1)<br />varwin = Titanium.UI.createWindow({ <br />title:'Hello',<br />backgroundColor:'#fff'<br />});<br />var label1 = Titanium.UI.createLabel({<br />color:'#333',<br />text:’Hello World!',<br />textAlign: 'center',<br />font: {fontSize: 30, fontWeight: 'bold'}<br />});<br />win.add(label1);<br />
  8. 8. app.js (2)<br />varbt = Titanium.UI.createButton({<br />title: 'Click me', <br />width: 100,<br />height: 40,<br />bottom: 40<br />})<br />bt.addEventListener('click', function(e) {<br />label1.text = 'WHYMCA ROCKS!';<br />});<br />win.add(bt);<br />win.open();<br />
  9. 9. UI widgetscreated in JS are mapped on the native componentsof the target platform<br />
  10. 10. Appdistribution<br />The productreadyfordistributionisactually a native app<br />JS Code<br />Partiallycompiled and optimized<br />Packed in binary format into the bundle<br />Resources<br />Copiedinto the bundle<br />
  11. 11. Developmenttoolchain<br />Ti Studio (IDE)<br />Ti Developer (toolchain GUI)<br />Ti SDK toolchain<br />(Pythonscripts)<br />Xcode/iOS SDK<br />Android SDK<br />Mac OSX<br />Windows<br />Linux<br />Mac OSX<br />
  12. 12. ApplicationStack<br />JavaScriptApplication Code<br />TitaniumJavaScript API<br />Android<br />Modules<br />iOS<br />Modules<br />JS Interpreter<br />JS Interpreter<br />TitaniumFramework<br />Runtime<br />Runtime<br />Android SDK<br />iOS SDK<br />
  13. 13. Ti JavaScript API<br />UI<br />Titanium.UI <br />Titanium.UI.Android <br />Titanium.UI.Clipboard <br />Titanium.UI.iOS <br />Titanium.UI.iPad <br />Titanium.UI.iPhone<br />Titanium.Map<br />Sensors<br />Titanium.Accelerometer<br />Titanium.Geolocation<br />Titanium.Gesture<br />Networking<br />Titanium.Network <br />Titanium.XML<br />Titanium.Facebook<br />Titanium.Yahoo<br />Titanium.Analytics<br />Device integration<br />Titanium.Platform<br />Titanium.Contacts <br />Titanium.Media <br />Titanium.Android <br />Titanium.Android.Calendar <br />Titanium.Android.NotificationManager<br />Titanium.App.iOS <br />Titanium.App.Android <br />Data Persistence<br />Titanium.Database <br />Titanium.Filesystem<br />Titanium.App.Properties <br />i18n<br />Titanium.Locale <br />Utilities/helpers<br />Titanium <br />Titanium.App <br />Titanium.API <br />Titanium.Utils<br />http://developer.appcelerator.com/apidoc/mobile<br />
  14. 14. Extending the API:why?<br />Accessingspecific OS features<br />Leveragingexisting native libraries<br />Optimizingcriticalportionsof the app<br />Extending/amelioratingportionsof the Titanium Mobile framework<br />
  15. 15. Extending the API:how?<br />Creating a forkofTitanium Mobile’ssource code on github<br />Unflexibleapproach<br />You put yourhands in the heartof the framework<br />Maintaining a separate branch can betedious and costly<br />There are situationswherethisis the cheaperapproach<br />E.g. whenneedingtoextend the functionalityofcoremodulesof the framework (networking, maps, ecc.) <br />
  16. 16. Extending the API:how?<br />Creatingone or more native modulesthrought the TitaniumModule SDK<br />Great flexibility<br />Easy todistributeas<br />Open Source<br />Binarypackages<br />AppceleratorTi+PlusMarketplace (?)<br />
  17. 17. Moduli nativi– some examples<br />Androidbarcode scanner (Zxingwrapper)<br />https://github.com/mwaylabs/titanium-barcode<br />iOSZipFile(create/decompress zip files)<br />https://github.com/TermiT/ZipFile<br />iOSTiStoreKit (in apppurchase)<br />https://github.com/masuidrive/TiStoreKit<br />iOSTiSMSDialog (in app sms sending)<br />https://github.com/omorandi/TiSMSDialog<br />AppceleratorTitaniummodules(sample modules)<br />https://github.com/appcelerator/titanium_modules<br />
  18. 18. Titanium JS Interface<br />varbt = Titanium.UI.createButton({<br />title: 'Click me', <br />width: 100,<br />height: 40,<br />bottom: 40<br />});<br />bt.addEventListener('click', function(e) {<br />label1.text = 'WHYMCA ROCKS!';<br />});<br />
  19. 19. Titanium JS Interface<br />Module<br />Titanium.UI<br />Object<br />Titanium.UI.Button<br />ObjectFactory<br />Titanium.UI.createButton()<br />Propertygetters/setters - methods<br />Button.title<br />Button.width<br />Button.animate()<br />Ecc.<br />Eventhandling<br />Button.addEventListener()<br />
  20. 20. ModuleArchitecture<br />Object<br />Objectproperty/method<br />Namespace<br />Titanium.UI<br />Titanium.UI.Button<br />Titanium.UI.Button.width<br />JavaScript<br />Module<br />Proxy<br />setters/getters<br />Methods<br />Events<br />setters/getters<br />Methods<br />Events<br />Internal State<br />Internal State<br />Titanium<br />abstraction<br />Implementation<br />Factory<br />Proxy Class<br />ModuleClass<br />
  21. 21. The pathtomoduledevelopment<br />Define the functionality you need to be exposed and how they’ll be invoked from JavaScript code  define the API of the module<br />Create a project with the toolsof the Module SDK<br />Implement the API<br />Build-Test-Debug<br />
  22. 22. Case Study–iOS SMS module<br />The Titanium API providesemailsendingfunctionalitythrough the Ti.UI.EmailDialogcomponent, butnothingforsending SMS messages<br />On iOSthisfeatureisavailablesinceversion 4.0 of the OS through the MFMessageComposeViewControllerclass<br />
  23. 23. MFMessageComposeViewController<br />
  24. 24. Case Study–iOS SMS module<br />Implement a native modulecapableofexposing the featuresof the MFMessageComposeViewControllerclassthrough a JavaScript API:<br />Check the availabilityof the component (notpresent in iOSversions < 4.0)<br />Programmatically set recipients and message body<br />Set UI properties (e.g. navbar color)<br />Notify the callerabout the resultof the sendoperation<br />
  25. 25. Resources<br />Module SDK Docs<br />Android<br />http://wiki.appcelerator.org/display/guides/Module+Developer+Guide+for+Android<br />iOS<br />http://wiki.appcelerator.org/display/guides/Module+Developer+Guide+for+iOS<br />Titanium Mobile source code<br />https://github.com/appcelerator/titanium_mobile<br />Code ofothermodulesreleasedas open source<br />
  26. 26. 1.Defining the API<br />Object<br />SMSDialog<br />Properties<br />recipients<br />messageBody<br />barColor<br />Methods<br />isSupported()<br />open()<br />
  27. 27. Example code<br />varsmsDialog = module.createSMSDialog();<br />if(smsDialog.isSupported()) <br />{<br />smsDialog.recipients = [’+14151234567'];<br />smsDialog.messageBody = 'Test messagefrom me';<br />smsDialog.barColor = ’red';<br />smsDialog.addEventListener('complete', function(e){<br />Ti.API.info('Result: ' + e.resultMessage);<br /> });<br />smsDialog.open({animated: true});<br />}<br />
  28. 28. Result<br />
  29. 29. 2.Creating the project<br /># titanium create <br />--platform=iphone <br />--type=module <br />--dir=~/<br />--name=SMSDialog <br />--id=com.whymca.smsdialog<br />
  30. 30. titanium (alias)<br />Mac OS X<br />alias titanium="/Library/Application Support/Titanium/mobilesdk/osx/1.7.0/titanium.py"<br />Linux<br />alias titanium="$HOME/Library/Application Support/Titanium/mobilesdk/osx/1.7.0/titanium.py"<br />Windows XP<br />PATH=C:Documents and SettingsAll UsersApplication DataTitaniummobilesdkwin321.7.0<br />Windows Vista/7<br />PATH=C:ProgramDataTitaniummobilesdkwin321.7.0<br />
  31. 31. Xcode project generated<br />assets/ <br />Classes/<br />ComWhymcaSmsdialogModule.h<br />ComWhymcaSmsdialogModule.m<br />ComWhymcaSmsdialogModuleAssets.h<br />ComWhymcaSmsdialogModuleAssets.m<br />example/<br />app.js<br />build.py<br />ComWhymcaSmsdialog_Prefix.pch <br />manifest<br />module.xcconfig<br />smsdialog.xcodeproj<br />titanium.xcconfig<br />Assetfilestobedistributedwith the module<br />Implementationfiles<br />Tobefilledwithour code<br />Sample programfortestingmoduleinvocations<br />Script for building and packaging the module<br />Metadataformanaging the module in Titanium<br />Include/linkingdirectivesused at integration-time<br />Include/linkingdirectivesused at build-time<br />
  32. 32. 3.Implementation<br />The generated project alreadycontains the classthatimplements the module:<br />ComWhymcaSmsdialogModule<br />The classnameis a CamelCasetranspositionof the moduleidchosenwhencreating the project<br />In JSitwillbeinstantiatedas<br />varmodule = require(‘com.whymca.smsdialog’);<br />
  33. 33. Implementing the proxy<br />The SMSDialogobjectwedefined, mustbeimplementedas a proxy<br />The nameof the classmustfollow a convention similartothatusedfor the modulename, in the form:<br /><moduleID><proxyName>Proxy<br />i.e.<br />ComWhymcaSmsdialogSMSDialogProxy<br />
  34. 34. SMSDialogProxyclass<br />Mustbe a subclassof the TiProxyclass<br />Itshouldexposeproperties and methodsthatwewanttomakeavailabletoJavaScript code<br />
  35. 35. Objectcreation and initialization<br />varsmsDialog = module.createSMSDialog();<br />smsDialog.recipients = [’+391234567'];<br />smsDialog.messageBody = 'Test message';<br />//or<br />varsmsDialog = module.createSMSDialog({<br />recipients: [’+391234567’],<br />messageBody: 'Test message’<br />});<br />
  36. 36. Creation<br />Objectcreationiscompletelymanagedby the framework<br />The JS construct<br />module.create<ProxyName>()<br />ismapped on code thatinstantiateanobjectof the correctclassthanksto the naming convention used<br />
  37. 37. Initialization<br />The object can beinitializedeitherpassing a dictionaryofpropertyvaluesto the factorymethod, eithersetting single propertyvalues in a second moment through dot notation<br />In either case, if the proxydoesn’texplicitlyprovidegetter/setter methodsforthoseproperties, they are automaticallyinserted in the dynpropsdictionaryof the proxyobject<br />
  38. 38. Initialization<br />It’salwayspossibletoretrievepropertyvaluespresent in the dynpropsdictionarywith the message<br />[self valueForUndefinedKey:@"messageBody"]<br />
  39. 39. Conversion utilities<br />The frameworkprovides the TiUtilsclass, whichcontainsmethodsforsimplifying the conversionofvaluescomingform JS code:<br />NSString * messageBody = [TiUtilsstringValue:[self valueForUndefinedKey:@"messageBody"]];<br />UIColor * barColor = [[TiUtilscolorValue:[self valueForUndefinedKey:@"barColor"]] _color];<br /> BOOL animated = [TiUtilsboolValue:@"animated" properties:argsdef:YES];<br />
  40. 40. Methods<br />They are exposedtoJavaScriptbysimplydeclaringthem in the @interfacesectionof the proxyclass<br />Theymustbedeclared in oneof the followingforms:<br />-(id)methodName:(id)args<br />-(void)methodName:(id)args<br />
  41. 41. Example<br />@interfaceComWhymcaSmsdialogSMSDialogProxy: TiProxy <MFMessageComposeViewControllerDelegate> <br />- (id)isSupported:(id)args;<br />- (void)open:(id)args;<br />@end<br />
  42. 42. Methodarguments<br />The listofargumentspassed in JavaScriptto a methodispassedas a NSArrayobject in the argsparameter<br />The frameworkprovides some Cmacrosfor the caseswhere a single argumentisexpected:<br />ENSURE_SINGLE_ITEM(args,type)<br />ENSURE_SINGLE_ARG_OR_NIL(args, type)<br />
  43. 43. PassingJavaScriptdictionaryObjects<br />JS:<br />smsDialog.open({animated: true});<br />OBJ-C:<br />- (void)open:(id)args {<br /> ENSURE_SINGLE_ARG_OR_NIL(args, NSDictionary);<br /> //args ora è un NSDictionary<br /> BOOL animated = [TiUtilsboolValue:@"animated" properties:argsdef:YES];<br />//[…]<br />
  44. 44. Returnvalues<br />The types<br />NSString <br />NSDictionary <br />NSArray <br />NSNumber <br />NSDate <br />NSNull<br /> don’tneedtobeconverted<br />Numerictypesmustbewrapped in a NSNumberobject<br />It’spossibletoreturnproxyobjects (thoughtheymustbeautoreleasedifallocatedbyourmethod)<br />return [[[TiColor alloc] initWithColor:colorname:@"#fff"] autorelease];<br />
  45. 45. Executing a method in the UI thread<br />There are caseswhere a methodshouldbeexecuted in the UI thread (e.g. wheninteractingwithuser interface objects)<br />In suchcaseswe can use the macro<br />ENSURE_UI_THREAD(method,args)<br />for forcing the executionof the methodon the UI thread<br />
  46. 46. Events<br />The simplest way a proxyhastointeractwith JS code isthroughevents<br />In JS weregisteranevent-listenerfunction on a specificeventmanagedby the proxy<br />Example<br />JS:<br />smsDialog.addEventListener('complete', function(e){<br />Ti.API.info('Result: ' + e.resultMessage);<br /> });<br />
  47. 47. Events<br />OBJ-C:<br />-(void)_listenerAdded:(NSString*)typecount:(int)count<br />{<br />//type = @"complete"<br />}<br />-(void)_listenerRemoved:(NSString*)typecount:(int)count {<br />//type = @"complete" <br />}<br />
  48. 48. Events<br />OBJ-C:<br />NSDictionary*event = [NSDictionarydictionaryWithObjectsAndKeys:resultMessage, @"resultMessage", nil];<br />[self fireEvent:@"complete" withObject:event];<br />
  49. 49. 4. Build<br />We can build the project directlyfromXcode<br />Usefulforchecking out warning & syntaxerrormessages<br />
  50. 50. ⌘B<br />
  51. 51. 4. Build<br />Using the script<br /> # build.py<br />from the module project root directory<br />Itperforms a complete build and itpackages the modulelibrary + assets + metadata in a zip file<br />
  52. 52. Package<br />Name in the form<br />com.whymca.smsdialog-iphone-0.1.zip<br />Forbeinguseditmustbedecompressed in the directory<br />/Library/ApplicationSupport/Titanium/<br />
  53. 53. Simpletesting<br />Byexecuting the script<br /># titaniumrun<br /> in the project directory<br />Thiswill create a temporary project based on the app.js file from the exampledirectory<br />Not the best way fortesting the module<br />
  54. 54. Using the module in a Ti Mobile Project<br /><?xml version="1.0" encoding="UTF-8" standalone="no"?><br /><ti:appxmlns:ti="http://ti.appcelerator.org"><br /><!–-SNIP…--><br /><modules><br /> <moduleversion=“0.1” platform=“iphone”><br />com.whymca.smsdialog<br /> </module><br /></modules><br /></ti:app><br />tiapp.xml<br />
  55. 55. Testing/Debugging<br />Create a new Ti Mobile project with test code and a referenceto the module<br />Launch the app at leastonetimefrom Ti Studio/Developer<br />Open the Xcode project from the build/iphonedirectory found in the app project directory<br />IssueBuild&Debugfromthere<br />Set breakpoints in the module code, test, etc.<br />
  56. 56.
  57. 57. Thankyou!<br />Anyquestions?<br />You can findall the code usedforthispresentation on<br />https://github.com/omorandi/whymca-conf-2011<br />

×