Extending Titanium

         Native iOS & Android
              Modules

tiConf.eu, valencia, 24/02/2013
Olivier Morandi
       Software engineer

          http://titaniumninja.com
          olivier.morandi@gmail.com
          @olivier_morandi
          https://github.com/omorandi




tiConf.eu, valencia, 24/02/2013         2
Module
    development is so
          2010
tiConf.eu, valencia, 24/02/2013
Why Bother?

   • To leverage native features
     ★ Underlying OS
     ★ 3rd party libraries
   • Performance
     ★ To optimize the User Experience


tiConf.eu, valencia, 24/02/2013          4
Learning Resources


tiConf.eu, valencia, 24/02/2013
Official Appcelerator Guides

   • http://docs.appcelerator.com/titanium/latest/#!/guide/
      Extending_Titanium_Mobile

   • http://docs.appcelerator.com/titanium/latest/#!/guide/
      iOS_Module_Development_Guide

   • http://docs.appcelerator.com/titanium/latest/#!/guide/
      Android_Module_Development_Guide




tiConf.eu, valencia, 24/02/2013                               6
tiConf.eu, valencia, 24/02/2013   7
Source code

   • Titanium Mobile SDK
     ★ https://github.com/appcelerator/
        titanium_mobile
   • Example modules from Appcelerator
     ★ https://github.com/appcelerator/
        titanium_modules


tiConf.eu, valencia, 24/02/2013           8
Follow these guys
   •   Aaron K. Saunders: https://github.com/aaronksaunders

   •   Ben Bahrenburg: https://github.com/benbahrenburg

   •   Christian Sullivan: https://github.com/euforic

   •   David Bankier: https://github.com/dbankier

   •   Jordi Domenec: https://github.com/iamyellow

   •   Marcel Pociot: https://github.com/mpociot

   •   Matt Apperson: https://github.com/mattapperson

   •   Paul Mietz Egli: https://github.com/pegli

   •   Ruben Fonseca: https://github.com/rubenfonseca

   •   Russ Frank: https://github.com/russfrank



tiConf.eu, valencia, 24/02/2013                               9
Inside Titanium
                (A bit of architecture)




tiConf.eu, valencia, 24/02/2013
Titanium cli (node.js) + python scripts




tiConf.eu, valencia, 24/02/2013                             11
Runtime (iOS)

                                                        JS APP



                                           Parser




                                                           KROLL BRIDGE
                        JavaScriptCore
                                         Bytecode                         Titanium
                                                                          Modules
                                                                                     objective-c
                                           gen
                                                                            (API)
                                                                                        C++
                                         Interpreter



                                                       IOS SDK




tiConf.eu, valencia, 24/02/2013                                                              12
Runtime (iOS)

                                                            JS APP



                                               Parser




                                                               KROLL BRIDGE
                        JavaScriptCore
                                             Bytecode                         Titanium
                                                                              Modules
                                                                                         objective-c
                                               gen
                                                                                (API)
                                                                                            C++
                                          O JIT
                                         N   Interpreter



                                                           IOS SDK




tiConf.eu, valencia, 24/02/2013                                                                  12
Runtime (Android)

                                          JS APP


                     V8
                              Parser




                                             KROLL BRIDGE
                            Native Code                     Titanium
                                                            Modules
                                                                       Java
                                gen
                      OPT
                                                              (API)
                                                                       C++
                            Native Code



                                       Android SDK




tiConf.eu, valencia, 24/02/2013                                               13
Runtime (Android)

                                          JS APP


                     V8
                              Parser




                                             KROLL BRIDGE
                            Native Code                     Titanium
                                                            Modules
                                                                       Java
                                gen
                      OPT
                                                              (API)
                                                                       C++
                            Native Code



                                       Android SDK




tiConf.eu, valencia, 24/02/2013                                               13
Runtime (Android)

                                          JS APP


                     V8
                              Parser




                                             KROLL BRIDGE
                            Native Code                     Titanium
                                                            Modules
                                                                       Java
                                gen
                      OPT
                                                              (API)
                                                                       C++
                            Native Code



                                       Android SDK




tiConf.eu, valencia, 24/02/2013                                               13
Terminology

                  var win1 = Titanium.UI.createWindow({
                      title:'Hello World',
                      backgroundColor:'white'
                  });

                  var label1 = Titanium.UI.createLabel({
                      color:'black',
                      textAlign:'center',
                      width: 100
                  });

                  label1.text = 'howdy?';
                  win1.add(label1);

                  win1.open();




tiConf.eu, valencia, 24/02/2013                            14
Terminology
                                 module
                                 object
                  var win1 = Titanium.UI.createWindow({
                      title:'Hello World',
                      backgroundColor:'white'
                  });

                  var label1 = Titanium.UI.createLabel({
                      color:'black',
                      textAlign:'center',
                      width: 100
                  });

                  label1.text = 'howdy?';
                  win1.add(label1);

                  win1.open();




tiConf.eu, valencia, 24/02/2013                            14
Terminology
                                 module
                                 object
                  var win1 = Titanium.UI.createWindow({
                      title:'Hello World',
                      backgroundColor:'white'
                  });

                  var label1 = Titanium.UI.createLabel({
                      color:'black',
                      textAlign:'center',
                      width: 100
                  });

                  label1.text = 'howdy?';
                  win1.add(label1);

                  win1.open();

                                                              he
                                                    cts  of t
                                               Obje       A PI a
                                                                 re
                                                   nium            S
                                              Tita        in the J
                                                   cted             pp
                                              inje         nt  at a
                                                   ro nme
                                              envi            p
                                                      s tartu



tiConf.eu, valencia, 24/02/2013                                          14
Terminology
                                            factory
                                            method
                  var win1 = Titanium.UI.createWindow({
                      title:'Hello World',
                      backgroundColor:'white'
                  });

                  var label1 = Titanium.UI.createLabel({
                      color:'black',
                      textAlign:'center',
                      width: 100
                  });

                  label1.text = 'howdy?';
                  win1.add(label1);

                  win1.open();




tiConf.eu, valencia, 24/02/2013                            14
Terminology

                  var win1 = Titanium.UI.createWindow({
                      title:'Hello World',                  creation
                      backgroundColor:'white'              properties
                  });

                  var label1 = Titanium.UI.createLabel({
                      color:'black',
                      textAlign:'center',
                      width: 100
                  });

                  label1.text = 'howdy?';
                  win1.add(label1);

                  win1.open();




tiConf.eu, valencia, 24/02/2013                                         14
Terminology
                    proxy
                    object
                  var win1 = Titanium.UI.createWindow({
                      title:'Hello World',
                      backgroundColor:'white'
                  });

                  var label1 = Titanium.UI.createLabel({
                      color:'black',
                      textAlign:'center',
                      width: 100
                  });

                  label1.text = 'howdy?';
                  win1.add(label1);

                  win1.open();




tiConf.eu, valencia, 24/02/2013                            14
Terminology

                       var win1 = Titanium.UI.createWindow({
                           title:'Hello World',
                           backgroundColor:'white'
                       });
          view proxy
                       var label1 = Titanium.UI.createLabel({
             object        color:'black',
                           textAlign:'center',
                           width: 100
                       });

                       label1.text = 'howdy?';
                       win1.add(label1);

                       win1.open();




tiConf.eu, valencia, 24/02/2013                                 14
Terminology

                      var win1 = Titanium.UI.createWindow({
                          title:'Hello World',
                          backgroundColor:'white'
                      });

                      var label1 = Titanium.UI.createLabel({
                          color:'black',
                          textAlign:'center',
                          width: 100
                      });
            proxy
                      label1.text = 'howdy?';
           property   win1.add(label1);

                      win1.open();




tiConf.eu, valencia, 24/02/2013                                14
Terminology

                    var win1 = Titanium.UI.createWindow({
                        title:'Hello World',
                        backgroundColor:'white'
                    });

                    var label1 = Titanium.UI.createLabel({
                        color:'black',
                        textAlign:'center',
                        width: 100
                    });

                    label1.text = 'howdy?';
                    win1.add(label1);
            proxy   win1.open();
           method




tiConf.eu, valencia, 24/02/2013                              14
Proxies & Modules

                                                    Native View Type
                      Proxy                             iOS UIView
                                                       Android View

  creates                                  manages
                     extends                                  extends

            Module             ViewProxy                  View


                                            has a


tiConf.eu, valencia, 24/02/2013                                        15
Proxies & Modules
                         Interface
        State:
            properties
        Actions:
            methods
        Events:
            addEventListener(), fireEvent()

                                                                   Native View Type
                                     Proxy                             iOS UIView
                                                                      Android View

  creates                                                 manages
                                 extends                                     extends

                Module                        ViewProxy                  View


                                                           has a


tiConf.eu, valencia, 24/02/2013                                                       15
Proxies & Modules
                                    Interface
                 State:
                     properties
                 Actions:
                     methods
                 Events:
                     addEventListener(), fireEvent()

                                                                             Native View Type
                                                Proxy                            iOS UIView
                                                                                Android View

    creates                                                         manages
                                           extends                                     extends

                          Module                        ViewProxy                  View

Methods for the integration within the
application lifecycle                                                has a
 •startup() (iOS)
 •shutdown() (iOS)
 •onAppCreate() (Android)




tiConf.eu, valencia, 24/02/2013                                                                 15
Proxies & Modules
                                    Interface
                 State:
                     properties
                 Actions:
                     methods
                 Events:
                     addEventListener(), fireEvent()

                                                                                                  Native View Type
                                                Proxy                                                 iOS UIView
                                                                                                     Android View

    creates                                                                              manages
                                           extends                                                          extends

                          Module                                  ViewProxy                             View
                                                Additional members for the integration
Methods for the integration within the
application lifecycle
                                                within the UI layout system:
                                                •add()
                                                                                          has a
 •startup() (iOS)                               •remove()
 •shutdown() (iOS)                              •height
 •onAppCreate() (Android)                       •width
                                                •backgroundColor
                                                •...


tiConf.eu, valencia, 24/02/2013                                                                                      15
Let’s create a module


tiConf.eu, valencia, 24/02/2013
• Create
   • Develop
   • Build
   • Deploy
   • Debug

tiConf.eu, valencia, 24/02/2013   17
• Create
   • Develop
   • Build
   • Deploy
   • Debug

tiConf.eu, valencia, 24/02/2013   17
Module Development


tiConf.eu, valencia, 24/02/2013
iOS Prerequisites


   • Same as for Ti app development on iOS:
     ★ Titanium SDK.
     ★ Xcode




tiConf.eu, valencia, 24/02/2013               19
Android Prerequisites
   •   Same as for Ti app development on Android:
       ★ Titanium SDK.
       ★ Android SDK (+ ANDROID_SDK environment variable)

   •   Additionally:
       ★ Android NDK (+ ANDROID_NDK environment variable)
       ★ Ant 1.7.1 (available in PATH)
       ★ gperf must be installed and in your system PATH.
       ★ [Eclipse]


tiConf.eu, valencia, 24/02/2013                             20
Create (cli)
   $ alias ti='~/Library/Application Support/Titanium/mobilesdk/osx/3.0.0.GA/
              titanium.py'




                                                            OL
                                                        SCHO
                                                    OLD


tiConf.eu, valencia, 24/02/2013                                                  21
Create (cli)
   $ alias ti='~/Library/Application Support/Titanium/mobilesdk/osx/3.0.0.GA/
              titanium.py'




   $ ti create --type=module --id=ti.conf.sample --name=ticonfsample
        --platform=iphone --dir=./ios




                                                            OL
                                                        SCHO
                                                    OLD


tiConf.eu, valencia, 24/02/2013                                                  21
Create (cli)
   $ alias ti='~/Library/Application Support/Titanium/mobilesdk/osx/3.0.0.GA/
              titanium.py'




   $ ti create --type=module --id=ti.conf.sample --name=ticonfsample
        --platform=iphone --dir=./ios




   $ ti create --type=module--id=ti.conf.sample --name=ticonfsample
        --platform=android --dir=./android




                                                            OL
                                                        SCHO
                                                    OLD


tiConf.eu, valencia, 24/02/2013                                                  21
tiConf.eu, valencia, 24/02/2013   22
tiConf.eu, valencia, 24/02/2013   22
tiConf.eu, valencia, 24/02/2013   22
tiConf.eu, valencia, 24/02/2013   22
Create (Ti Studio)




tiConf.eu, valencia, 24/02/2013     23
Create (Ti Studio)




tiConf.eu, valencia, 24/02/2013     24
Create (Ti Studio)




tiConf.eu, valencia, 24/02/2013     25
Module Boilerplate




tiConf.eu, valencia, 24/02/2013    26
Module Boilerplate




tiConf.eu, valencia, 24/02/2013    27
Build & Install (cli)

   $ ./build.py
   $ unzip -uo ti.conf.sample-iphone-0.1.zip -d ~/Library/Application Support/
   Titanium/




tiConf.eu, valencia, 24/02/2013                                                   28
Build & Install (cli)

   $ ./build.py
   $ unzip -uo ti.conf.sample-iphone-0.1.zip -d ~/Library/Application Support/
   Titanium/




   $ ant
   $ unzip -uo dist/ti.conf.sample-android-0.1.zip -d ~/Library/Application
   Support/Titanium/




tiConf.eu, valencia, 24/02/2013                                                   28
Build (Ti Studio)




tiConf.eu, valencia, 24/02/2013     29
Build & Install (Ti Studio)




tiConf.eu, valencia, 24/02/2013        30
Deploy
            tiapp.xml
             <?xml version="1.0" encoding="UTF-8"?>
             <ti:app xmlns:ti="http://ti.appcelerator.org">
               <id>com.omorandi.ticonftest</id>

              <!-- ... -->

               <modules>
                 <module platform="iphone">ti.conf.sample</module>
                 <module platform="android">ti.conf.sample</module>
               </modules>
             </ti:app>




            app.js
             var ticonfsample = require('ti.conf.sample');

             Ti.API.info(ticonfsample.example());

             Ti.API.info("module exampleProp is => " + ticonfsample.exampleProp);
             ticonfsample.exampleProp = "This is a test value";




tiConf.eu, valencia, 24/02/2013                                                     31
Agenda
   • Methods                      • Events
   • Passing                      • Callbacks
      arguments
                                  • ViewProxies
   • Returning values
   • Exceptions
   • Using properties
   • Proxies
tiConf.eu, valencia, 24/02/2013                   32
Proxy/Module Methods
                             return value
  -(id)methodName:(id)args
  {
      NSString result = @"Hello World”;

      //do something

      return result;
  }




tiConf.eu, valencia, 24/02/2013             33
Proxy/Module Methods
                               return value
  -(id)methodName:(id)args
  {
      NSString result = @"Hello World”;

      //do something

      return result;
  }



                          no return value
  -(void)methodName:(id)args
  {
      //do something
  }




tiConf.eu, valencia, 24/02/2013               33
Proxy/Module Methods
                                                                        return value
                               return value                               no args
  -(id)methodName:(id)args                    @Kroll.method
  {                                           public String methodName()
      NSString result = @"Hello World”;       {
                                                 String result = "Hello world";
      //do something
                                                  //do something
      return result;
  }                                               return result;
                                              }


                          no return value
  -(void)methodName:(id)args
  {
      //do something
  }




tiConf.eu, valencia, 24/02/2013                                                    33
Proxy/Module Methods
                                                                        return value
                               return value                               no args
  -(id)methodName:(id)args                    @Kroll.method
  {                                           public String methodName()
      NSString result = @"Hello World”;       {
                                                 String result = "Hello world";
      //do something
                                                  //do something
      return result;
  }                                               return result;
                                              }


                          no return value                            no return value
                                              @Kroll.method
  -(void)methodName:(id)args
                                              public void methodName(String value)
  {
                                              {
      //do something
                                                 //do something
  }
                                              }




tiConf.eu, valencia, 24/02/2013                                                      33
Example: xml2json module
        Expected API


          var xml2json = require('ti.xml2json');

          var json = xml2json.convert(xmlDoc);




                 https://github.com/omorandi/TiXml2Json


tiConf.eu, valencia, 24/02/2013                           34
Implementation
         @implementation TiXml2jsonModule

         -(NSDictionary) convertXml:(NSString)xmlString
         {
            NSDictionary *jsObj;

             //do conversion stuff

             return jsObj;
         }


         -(id)convert:(id)args
         {
             ENSURE_SINGLE_ARG(args, NSString);

             return [self convertXml:args];
         }

         @end




tiConf.eu, valencia, 24/02/2013                           35
Implementation
         @implementation TiXml2jsonModule

         -(NSDictionary) convertXml:(NSString)xmlString
         {
            NSDictionary *jsObj;

             //do conversion stuff

             return jsObj;
         }


         -(id)convert:(id)args NSArray of arguments
         {
             ENSURE_SINGLE_ARG(args, NSString);

             return [self convertXml:args];
         }

         @end




tiConf.eu, valencia, 24/02/2013                           35
Implementation
         @implementation TiXml2jsonModule

         -(NSDictionary) convertXml:(NSString)xmlString
         {
            NSDictionary *jsObj;

             //do conversion stuff

             return jsObj;
         }


                                                                            TiBase.h
         -(id)convert:(id)args NSArray of arguments
                                                      #define ENSURE_SINGLE_ARG(x,t)
         {                                            { 
             ENSURE_SINGLE_ARG(args, NSString);         x = (t*)[x objectAtIndex:0]; 
                                                      } 
             return [self convertXml:args];
         }

         @end




tiConf.eu, valencia, 24/02/2013                                                          35
Utility Macros
                                              TiBase.h
             #define   ENSURE_CLASS(x,t)
             #define   ENSURE_CLASS_OR_NIL(x,t)
             #define   ENSURE_TYPE(x,t)
             #define   ENSURE_TYPE_OR_NIL(x,t)
             #define   ENSURE_ARG_COUNT(x,c)
             #define   ENSURE_SINGLE_ARG(x,t)
             #define   ENSURE_SINGLE_ARG_OR_NIL(x,t)
             #define   ENSURE_DICT(x)
             #define   ENSURE_ARRAY(x)
             #define   ENSURE_STRING(x)
             #define   THROW_INVALID_ARG(m)

             ...




tiConf.eu, valencia, 24/02/2013                          36
Types

     Supported Directly                 Conversion Utilities
   NSString
   NSDictionary           #import "TiUtils.h"


   NSArray                CGFloat f = [TiUtils floatValue:arg];
                          NSInteger f = [TiUtils intValue:arg];
   NSNumber               NSString *value = [TiUtils stringValue:arg];
   NSDate                 TiColor *bgcolor = [TiUtils colorValue:arg];
   NSNull                 UIColor *backgroundColor = [bgcolor color];

   TiProxy



tiConf.eu, valencia, 24/02/2013                                          37
Return Values
   • Single Value (NSString, NSNumber, …)
   • Collections (NSArray)
     ★ Converted into a JS Array object
   • Dictionary (NSDictionary)
     ★ Converted into a JS object
     ★ key->value ===> property->value
   • Proxy (TiProxy)
tiConf.eu, valencia, 24/02/2013             38
Return Values
   • Single Value (NSString, NSNumber, …)
   • Collections (NSArray)
     ★ Converted into a JS Array object
   • Dictionary (NSDictionary)
     ★ Converted into a JS object                            TS
                                                         BJEC
     ★ key->value ===> property->value              DO
                                                  SE
                                                EA
                                             EL
                                           OR
   • Proxy (TiProxy)                 UR
                                       NA
                                         UT

                                  RET
tiConf.eu, valencia, 24/02/2013                               38
xml2json Android
          import org.appcelerator.kroll.KrollDict;


          @Kroll.module(name="Tixml2json", id="ti.xml2json")
          public class Tixml2jsonModule extends KrollModule
          {

              @Kroll.method
              public KrollDict convert(String xml)
              {
                 KrollDict json = null;

                   //do conversion stuff

                   return json;
              }

          }




tiConf.eu, valencia, 24/02/2013                                39
xml2json Android
          import org.appcelerator.kroll.KrollDict;


          @Kroll.module(name="Tixml2json", id="ti.xml2json")
          public class Tixml2jsonModule extends KrollModule
          {
                                                                              KrollDict.java
              @Kroll.method                           public class    KrollDict
                                                          extends    HashMap<String, Object>
              public KrollDict convert(String xml)
              {
                 KrollDict json = null;

                   //do conversion stuff

                   return json;
              }

          }




tiConf.eu, valencia, 24/02/2013                                                            39
Types

      Supported Directly                                             Conversion Utilities
String
int                                                 import org.appcelerator.titanium.util.TiConvert;

float                                               int val = TiConvert.toInt(obj);
double                                              float val = TiConvert.toFloat(obj);
boolean                                             double val = TiConvert.toDouble(obj);
                                                    boolean val = TiConvert.toBoolean(obj);
Object[]                                            int color = TiConvert.toColor(str);
HashMap<String, Object>                             ...
TiProxy




 http://builds.appcelerator.com.s3.amazonaws.com/module-apidoc/2.0.0/android/org/appcelerator/titanium/util/TiConvert.html



tiConf.eu, valencia, 24/02/2013                                                                                        40
Return Values
   • Single Value (String, Integer, …)
   • Collections (Object[])
     ★ Converted into a JS Array object
   • Dictionary (HashMap<String, Object>)
     ★ Converted into a JS object
     ★ key->value ===> property->value
   • Proxy (TiProxy)
tiConf.eu, valencia, 24/02/2013             41
Polymorphic Methods
      -(id)convert:(id)args
      {
          ENSURE_ARG_COUNT(args, 1);

          id arg = [args objectAtIndex:0];

          if ([arg isKindOfClass:[NSString class]]) {
              return [self convertFromString:arg];
          }
          else if ([arg isKindOfClass:[TiBlob class]]) {
              return [self convertFromData:arg];
          }
          else {
              [self throwException:@"Expected blob or string argument"
                 subreason:nil location:CODELOCATION];
          }
      }




tiConf.eu, valencia, 24/02/2013                                          42
Polymorphic Methods

       public KrollDict convertFromString(String xml);
       public KrollDict convertFromBlob(TiBlob blob)

       @Kroll.method
       public String convert(Object arg) {
          if (arg instanceof String) {
              return "string";
          }
          if (arg instanceof TiBlob) {
              return "blob";
          }
          throw new IllegalArgumentException("Invalid argument type,
              expected blob or string");
       }




tiConf.eu, valencia, 24/02/2013                                        43
Varargs

        -(void) varArgsMethod:(id)args
        {

            for (int i = 0; i < [args count]; i++) {
                id arg = [args objectAtIndex:i];
                // do something with arg
            }
        }




tiConf.eu, valencia, 24/02/2013                        44
Varargs

       @Kroll.method
       public void varArgsMethod(Object[] args) {
          for (int i = 0; i < args.length; i++) {
              Object arg = args[i];
              // do something with arg
          }
       }




tiConf.eu, valencia, 24/02/2013                     45
Properties


    set   module.propertyName = "HELLO";

    get   Ti.API.info("Property: " + module.propertyName);




tiConf.eu, valencia, 24/02/2013                              46
Properties
                                                            TiMyModule.h

     @interface TiMyModule: TiModule

     @property (nonatomic, readwrite, retain) NSString* propertyName;

     @end




                                                            TiMyModule.m
     @implementation TiMyModule: TiModule

     @synthesize propertyName;

     @end




tiConf.eu, valencia, 24/02/2013                                            47
Properties: Setter/Getter

       - (void) setPropertyName:(id)args {
           // set property and do stuff
       }

       - (id) propertyName {
           // do something and
           return value;
       }




tiConf.eu, valencia, 24/02/2013              48
Properties: Setter/Getter
       @Kroll.module(name="My", id="ti.my")
       public class MyModule extends KrollModule
       {
          private String propertyName;

           @Kroll.getProperty @Kroll.method
           public String getPropertyName() {
               return propertyName;
           }

           @Kroll.setProperty @Kroll.method
           public void setPropertyName(String value) {

               propertyName = value;
           }
       }




tiConf.eu, valencia, 24/02/2013                          49
Constants
          var smsModule = require('ti.ios.sms');

          function sendCallback(e){
             switch (e.result) {
                 case sms.SENT:
                    result = 'SENT';
                    break;
                 case sms.FAILED:
                    result = 'FAILED';
                    break;
                 case sms.CANCELLED:
                    result = 'CANCELLED';
                    break;
             }
             Ti.API.info("Property: " + module.propertyName);
          }




tiConf.eu, valencia, 24/02/2013                                 50
Constants
       https://github.com/omorandi/TiSMSDialog/blob/master/Classes/ComOmorandiSMSDialogProxy.m

       //create the accessor methods for the SENT, CANCELLED and FAILED
       constants
       MAKE_SYSTEM_PROP(SENT,MessageComposeResultSent);
       MAKE_SYSTEM_PROP(CANCELLED,MessageComposeResultCancelled);
       MAKE_SYSTEM_PROP(FAILED,MessageComposeResultFailed);




tiConf.eu, valencia, 24/02/2013                                                                  51
Constants
               https://github.com/omorandi/TiSMSDialog/blob/master/Classes/ComOmorandiSMSDialogProxy.m

              //create the accessor methods for the SENT, CANCELLED and FAILED
              constants
              MAKE_SYSTEM_PROP(SENT,MessageComposeResultSent);
              MAKE_SYSTEM_PROP(CANCELLED,MessageComposeResultCancelled);
              MAKE_SYSTEM_PROP(FAILED,MessageComposeResultFailed);
TiBase.h
#define MAKE_SYSTEM_PROP(name,map) 
-(NSNumber*)name 
{
return [NSNumber numberWithInt:map];
}




tiConf.eu, valencia, 24/02/2013                                                                          51
Constants
               https://github.com/omorandi/TiSMSDialog/blob/master/Classes/ComOmorandiSMSDialogProxy.m

              //create the accessor methods for the SENT, CANCELLED and FAILED
              constants
              MAKE_SYSTEM_PROP(SENT,MessageComposeResultSent);
              MAKE_SYSTEM_PROP(CANCELLED,MessageComposeResultCancelled);
              MAKE_SYSTEM_PROP(FAILED,MessageComposeResultFailed);
TiBase.h
#define MAKE_SYSTEM_PROP(name,map) 
-(NSNumber*)name 
{
return [NSNumber numberWithInt:map];
}




              @Kroll.module(name="Sms", id="ti.android.sms")
              public class SmsModule extends KrollModule
              {
                  @Kroll.constant
                  public static final int SENT = 0;
                  @Kroll.constant
                  public static final int CANCELLED = -1;
                  @Kroll.constant
                  public static final int FAILED = -2;
              }



tiConf.eu, valencia, 24/02/2013                                                                          51
Proxy Objects

          var smsModule = require('ti.ios.sms');

          //create the smsDialog object
          var smsDialog = smsModule.createSMSDialog({
              recipients: ['+123456789'],
              messageBody: 'hello'
          });

          smsDialog.open();




tiConf.eu, valencia, 24/02/2013                         52
Creating a Proxy
                                                                               TiIosSmsSMSDialogProxy.h
 @interface TiIosSmsSMSDialogProxy: TiProxy<MFMessageComposeViewControllerDelegate>


 @end




                                                                              TiIosSmsSMSDialogProxy.m
 @implementation TiIosSmsSMSDialogProxy

 - (void)open:(id)args
 {

        // retrieve properties (either set on creation, or later)

        NSArray * recipients = [self valueForUndefinedKey:@"recipients"];
        NSString * messageBody= [TiUtils stringValue:[self valueForUndefinedKey:@"messageBody"]];

        // do stuff

 }

 @end




tiConf.eu, valencia, 24/02/2013                                                                           53
Proxy Objects

          var smsModule = require('ti.android.sms');

          //create the sms object
          var sms = smsModule.createSms({
              recipient: '+123456789',
              messageBody: 'hello'
          });

          sms.send();




tiConf.eu, valencia, 24/02/2013                        54
Creating a Proxy
       @Kroll.proxy(creatableInModule=SmsModule.class)
       public class SmsProxy extends KrollProxy
       {
            private String messageBody = null;
            private String recipient = null;

           // Constructor
           public SmsProxy() {
                super();
           }

           // Handle creation options
           @Override
           public void handleCreationDict(KrollDict options) {
                super.handleCreationDict(options);

                if (options.containsKey("messageBody")) {
                     messageBody = (String)options.get("messageBody");
                }

                if (options.containsKey("recipient")) {
                     recipient = (String)options.get("recipient");
                }
           }


           @Kroll.method
           public void send()
           {
                  // send the message
           }
       }


tiConf.eu, valencia, 24/02/2013                                          55
Events
      Notify a change of state, or an asynchronous event

            // create the Module object
            var tibarcode = require('ti.barcode');

            var scanner = tibarcode.createScanner();

            // success event listener
            scanner.addEventListener('success', function(e) {
                var code = e.barcode;
                var type = e.type;
                alert('Found code: ' + code + ' type: ' + type);
            });




tiConf.eu, valencia, 24/02/2013                                    56
Events

      @implementation TiBarcodeScannerProxy


      // Scanner Delegate

      - (void) imagePickerController: (UIImagePickerController*)reader
           didFinishPickingMediaWithCode:(NSString*)code andType:(NSString*)type
      {
         if ([self _hasListeners:@"success"]){
              NSDictionary *results = [NSDictionary dictionaryWithObjectsAndKeys:
                      code, @"code", type, @"type", nil];

              [self fireEvent:@"success" withObject:results];
          }
      }

      @end




tiConf.eu, valencia, 24/02/2013                                                     57
Events

       public class ScannerProxy extends KrollProxy
       {
          void onScannerResult(String code, String type)
          {
              if (hasListeners("success")) {
                 KrollDict event = new KrollDict();
                 event.put("code", code);
                 event.put("type", type);
                 fireEvent("success", event);
              }
          }
       }




tiConf.eu, valencia, 24/02/2013                            58
Callbacks
        Notify the result of an asynchronous action



        // let's not freeze on huge xml data
        xml2json.convertAsync(xmlDoc, function(data) {
            Ti.API.info("JSON object: " + JSON.stringify(data.json));
        });




tiConf.eu, valencia, 24/02/2013                                         59
Callbacks

      -(void)convertAsync:(id)args
      {
          ENSURE_ARG_COUNT(args, 2);

          id xml = [args objectAtIndex:0];
          KrollCallback *cb = [args objectAtIndex:1];
          ENSURE_TYPE(cb, KrollCallback);

          //pass just the first (string|blob) arg to convertXml()

          dispatch_async(dispatchQueue, ^(void) {
              id result = [self convertXml:xml];
              NSDictionary *cbArgs = [NSDictionary dictionaryWithObject:result forKey:@"json"];

                [self _fireEventToListener:@"success" withObject:cbArgs listener:cb
                     thisObject:nil];
          });
      }




tiConf.eu, valencia, 24/02/2013                                                                   60
Callbacks

     @Kroll.method
     public KrollDict convertAsync(String xml, final KrollFunction callback)
     {
        new Thread() {
            @Override public void run() {
               KrollDict json = null;

               //do conversion stuff

               KrollDict data = new KrollDict();
               event.put("json", json);
               callback.call(getKrollObject(), data);
            }
         }.start();
     }




tiConf.eu, valencia, 24/02/2013                                                61
ViewProxy
                                      Manages the
                  Holds the state
                                      native view
                    of a view
                                       hierarchy

     Methods




                                                    Native Views
                        ViewProxy




                                                     Hierarchy
     Properties




                                         View
      (get/set)

      Events




tiConf.eu, valencia, 24/02/2013                                    62
ViewProxy
                                      Manages the
                  Holds the state
                                      native view
                    of a view
                                       hierarchy

     Methods




                                                    Native Views
                        ViewProxy




                                                     Hierarchy
     Properties




                                         View
      (get/set)

      Events




                   JS THREAD




tiConf.eu, valencia, 24/02/2013                                    62
ViewProxy
                                      Manages the
                  Holds the state
                                      native view
                    of a view
                                       hierarchy

     Methods




                                                    Native Views
                        ViewProxy




                                                     Hierarchy
     Properties




                                         View
      (get/set)

      Events




                   JS THREAD          UI THREAD




tiConf.eu, valencia, 24/02/2013                                    62
ViewProxy
                                             Manages the
                  Holds the state
                                             native view
                    of a view
                                              hierarchy

     Methods




                                                           Native Views
                        ViewProxy




                                                            Hierarchy
     Properties




                                                   View
      (get/set)

      Events




                   JS THREAD                  UI THREAD

                                    Mostly async



tiConf.eu, valencia, 24/02/2013                                           62
Super-Smooth TableView

• API
    • createMessagesView(properties);
    • setMessages([]messages);
    • insert(message);
    • addEventListener(‘click’, callback);




tiConf.eu, valencia, 24/02/2013              63
Implementation
                                       MessagesViewProxy

                                                                          TiSmoothMessagesViewProxy.h
      @interface TiSmoothMessagesViewProxy : TiViewProxy

      @property (nonatomic, retain) MessagesCollection *msgs;   //model

      @end


                                                                          TiSmoothMessagesViewProxy.m
      @implementation TiSmoothMessagesViewProxy

      @synthesize msgs;

      //other methods

      -(void) insert:(id)args
      {
          ENSURE_SINGLE_ARG(args, NSDictionary);
          [self.msgs insertMessageOnTop:[self messageFromDictionary:(NSDictionary*)args]];


          [self makeViewPerformSelector:@selector(addMessage:) withObject:args createIfNeeded:YES
               waitUntilDone:NO];
      }

      @end


tiConf.eu, valencia, 24/02/2013                                                                         64
Implementation
                                             MessagesViewProxy

                                                                           TiSmoothMessagesViewProxy.h
      @interface TiSmoothMessagesViewProxy : TiViewProxy

      @property (nonatomic, retain) MessagesCollection *msgs;    //model

      @end


                                                                           TiSmoothMessagesViewProxy.m
      @implementation TiSmoothMessagesViewProxy

      @synthesize msgs;

      //other methods

      -(void) insert:(id)args
      {
          ENSURE_SINGLE_ARG(args, NSDictionary);
          [self.msgs insertMessageOnTop:[self messageFromDictionary:(NSDictionary*)args]];

     create the view and call addMessage on the UI thread
           [self makeViewPerformSelector:@selector(addMessage:) withObject:args createIfNeeded:YES
                waitUntilDone:NO];
      }

      @end


tiConf.eu, valencia, 24/02/2013                                                                          64
Implementation
                                                      MessagesView
                                                                                                TiSmoothMessagesView.h
      @interface   TiSmoothMessagesView      : TiUIView
      {

          MessagesViewController *viewController;
      }

      @end



                                                                                                TiSmoothMessagesView.m
      @implementation TiSmoothMessagesView

      -(void)initializeState
      {
          [super initializeState];
          viewController = [[InboxViewController alloc] initWithStyle:UITableViewStylePlain];

          UITableView *tableView = viewController.tableView;

          [self addSubview:tableView];
      }

      -(void)addMessage:(InboxMessage*)message
      {
          ENSURE_UI_THREAD(addMessage, message);
          [viewController addMessageOnTop:message];
      }

      -(void)frameSizeChanged:(CGRect)frame bounds:(CGRect)bounds
      {
          [TiUtils setView: viewController.tableView positionRect:bounds];
      }

      @end



tiConf.eu, valencia, 24/02/2013                                                                                          65
Implementation
                                                      MessagesView
                                                                                                TiSmoothMessagesView.h
      @interface   TiSmoothMessagesView      : TiUIView
      {

          MessagesViewController *viewController;
      }

      @end



                                                                                                TiSmoothMessagesView.m
      @implementation TiSmoothMessagesView

      -(void)initializeState         called by Titanium at view creation
      {
          [super initializeState];
          viewController = [[InboxViewController alloc] initWithStyle:UITableViewStylePlain];

          UITableView *tableView = viewController.tableView;

          [self addSubview:tableView];
      }

      -(void)addMessage:(InboxMessage*)message
      {
          ENSURE_UI_THREAD(addMessage, message);
          [viewController addMessageOnTop:message];
      }

      -(void)frameSizeChanged:(CGRect)frame bounds:(CGRect)bounds
      {
          [TiUtils setView: viewController.tableView positionRect:bounds];
      }

      @end



tiConf.eu, valencia, 24/02/2013                                                                                          65
Implementation
                                                      MessagesView
                                                                                                TiSmoothMessagesView.h
      @interface   TiSmoothMessagesView      : TiUIView
      {

          MessagesViewController *viewController;
      }

      @end



                                                                                                TiSmoothMessagesView.m
      @implementation TiSmoothMessagesView

      -(void)initializeState         called by Titanium at view creation
      {
          [super initializeState];
          viewController = [[InboxViewController alloc] initWithStyle:UITableViewStylePlain];

          UITableView *tableView = viewController.tableView;

          [self addSubview:tableView];
      }

      -(void)addMessage:(InboxMessage*)message
      {
          ENSURE_UI_THREAD(addMessage, message);
          [viewController addMessageOnTop:message];
      }

      -(void)frameSizeChanged:(CGRect)frame bounds:(CGRect)bounds
                                                                     called by Titanium for notifying a
      {                                                              change in frame size
          [TiUtils setView: viewController.tableView positionRect:bounds];
      }

      @end



tiConf.eu, valencia, 24/02/2013                                                                                          65
module.xcconfig
      //
      // PLACE ANY BUILD DEFINITIONS IN THIS FILE AND THEY WILL BE
      // PICKED UP DURING THE APP BUILD FOR YOUR MODULE
      //

      OTHER_LDFLAGS=$(inherited) -framework AVFoundation
         -framework CoreMedia -framework CoreVideo -framework QuartzCore
         /usr/lib/libiconv.dylib



         build &
        package




     Module Package
         (.zip)




                                                                     app bundle
tiConf.eu, valencia, 24/02/2013                                                   66
module.xcconfig
      //
      // PLACE ANY BUILD DEFINITIONS IN THIS FILE AND THEY WILL BE
      // PICKED UP DURING THE APP BUILD FOR YOUR MODULE
      //

      OTHER_LDFLAGS=$(inherited) -framework AVFoundation
         -framework CoreMedia -framework CoreVideo -framework QuartzCore
         /usr/lib/libiconv.dylib



         build &
        package




     Module Package
         (.zip)




                                                                     app bundle
tiConf.eu, valencia, 24/02/2013                                                   66
timodule.xml
     <?xml version="1.0" encoding="UTF-8"?>
     <ti:module xmlns:ti="http://ti.appcelerator.org" xmlns:android="http://schemas.android.com/apk/res/android">
     	    <iphone>
     	    </iphone>
     	    <android xmlns:android="http://schemas.android.com/apk/res/android">
     	         <manifest>
     	    	     	     <uses-permission android:name="android.permission.SEND_SMS" />
                      <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
             	
                     	<application>
     	    	              <activity android:name="ti.conf.sample.MyCustomActivity">
     	    	              </activity>
     	    	         </application>
             </manifest>
     	    </android>
     	    <mobileweb>
     	    </mobileweb>
     </ti:module>




             build &
            package                                                                                AndroidManifest.xml




      Module Package
          (.zip)


                                                                                                            app.apk
tiConf.eu, valencia, 24/02/2013                                                                                            67
Debugging


tiConf.eu, valencia, 24/02/2013
Create a debug build
  $ sed s/Release/Debug/ build.py > build_debug.py




  build.py
  168:   rc = os.system("xcodebuild -sdk iphoneos -configuration Release")
  171:   rc = os.system("xcodebuild -sdk iphonesimulator -configuration Release")




  build_debug.py
  168:   rc = os.system("xcodebuild -sdk iphoneos -configuration Debug")
  171:   rc = os.system("xcodebuild -sdk iphonesimulator -configuration Debug")




tiConf.eu, valencia, 24/02/2013                                                     69
Debugging




tiConf.eu, valencia, 24/02/2013   70
Debug Logs
    @Kroll.module(name="Ticonfsample", id="ti.conf.sample")
    public class TiconfsampleModule extends KrollModule
    {

    	   // Tag for debug log messages
    	   private static final String LCAT = "TiconfsampleModule";



          // tells if debug logging has been enabled in the Titanium application
    	   private static final boolean DBG = TiConfig.LOGD;




        @Kroll.method
    	   public void doSomething()
    	   {
    	   	 Log.d(LCAT, "doing something");
    	   }
    }


tiConf.eu, valencia, 24/02/2013                                                    71
Android DDMS




tiConf.eu, valencia, 24/02/2013   72
Thank you!


tiConf.eu, valencia, 24/02/2013

Extending Titanium with native iOS and Android modules

  • 1.
    Extending Titanium Native iOS & Android Modules tiConf.eu, valencia, 24/02/2013
  • 2.
    Olivier Morandi Software engineer http://titaniumninja.com olivier.morandi@gmail.com @olivier_morandi https://github.com/omorandi tiConf.eu, valencia, 24/02/2013 2
  • 3.
    Module development is so 2010 tiConf.eu, valencia, 24/02/2013
  • 4.
    Why Bother? • To leverage native features ★ Underlying OS ★ 3rd party libraries • Performance ★ To optimize the User Experience tiConf.eu, valencia, 24/02/2013 4
  • 5.
  • 6.
    Official Appcelerator Guides • http://docs.appcelerator.com/titanium/latest/#!/guide/ Extending_Titanium_Mobile • http://docs.appcelerator.com/titanium/latest/#!/guide/ iOS_Module_Development_Guide • http://docs.appcelerator.com/titanium/latest/#!/guide/ Android_Module_Development_Guide tiConf.eu, valencia, 24/02/2013 6
  • 7.
  • 8.
    Source code • Titanium Mobile SDK ★ https://github.com/appcelerator/ titanium_mobile • Example modules from Appcelerator ★ https://github.com/appcelerator/ titanium_modules tiConf.eu, valencia, 24/02/2013 8
  • 9.
    Follow these guys • Aaron K. Saunders: https://github.com/aaronksaunders • Ben Bahrenburg: https://github.com/benbahrenburg • Christian Sullivan: https://github.com/euforic • David Bankier: https://github.com/dbankier • Jordi Domenec: https://github.com/iamyellow • Marcel Pociot: https://github.com/mpociot • Matt Apperson: https://github.com/mattapperson • Paul Mietz Egli: https://github.com/pegli • Ruben Fonseca: https://github.com/rubenfonseca • Russ Frank: https://github.com/russfrank tiConf.eu, valencia, 24/02/2013 9
  • 10.
    Inside Titanium (A bit of architecture) tiConf.eu, valencia, 24/02/2013
  • 11.
    Titanium cli (node.js)+ python scripts tiConf.eu, valencia, 24/02/2013 11
  • 12.
    Runtime (iOS) JS APP Parser KROLL BRIDGE JavaScriptCore Bytecode Titanium Modules objective-c gen (API) C++ Interpreter IOS SDK tiConf.eu, valencia, 24/02/2013 12
  • 13.
    Runtime (iOS) JS APP Parser KROLL BRIDGE JavaScriptCore Bytecode Titanium Modules objective-c gen (API) C++ O JIT N Interpreter IOS SDK tiConf.eu, valencia, 24/02/2013 12
  • 14.
    Runtime (Android) JS APP V8 Parser KROLL BRIDGE Native Code Titanium Modules Java gen OPT (API) C++ Native Code Android SDK tiConf.eu, valencia, 24/02/2013 13
  • 15.
    Runtime (Android) JS APP V8 Parser KROLL BRIDGE Native Code Titanium Modules Java gen OPT (API) C++ Native Code Android SDK tiConf.eu, valencia, 24/02/2013 13
  • 16.
    Runtime (Android) JS APP V8 Parser KROLL BRIDGE Native Code Titanium Modules Java gen OPT (API) C++ Native Code Android SDK tiConf.eu, valencia, 24/02/2013 13
  • 17.
    Terminology var win1 = Titanium.UI.createWindow({ title:'Hello World', backgroundColor:'white' }); var label1 = Titanium.UI.createLabel({ color:'black', textAlign:'center', width: 100 }); label1.text = 'howdy?'; win1.add(label1); win1.open(); tiConf.eu, valencia, 24/02/2013 14
  • 18.
    Terminology module object var win1 = Titanium.UI.createWindow({ title:'Hello World', backgroundColor:'white' }); var label1 = Titanium.UI.createLabel({ color:'black', textAlign:'center', width: 100 }); label1.text = 'howdy?'; win1.add(label1); win1.open(); tiConf.eu, valencia, 24/02/2013 14
  • 19.
    Terminology module object var win1 = Titanium.UI.createWindow({ title:'Hello World', backgroundColor:'white' }); var label1 = Titanium.UI.createLabel({ color:'black', textAlign:'center', width: 100 }); label1.text = 'howdy?'; win1.add(label1); win1.open(); he cts of t Obje A PI a re nium S Tita in the J cted pp inje nt at a ro nme envi p s tartu tiConf.eu, valencia, 24/02/2013 14
  • 20.
    Terminology factory method var win1 = Titanium.UI.createWindow({ title:'Hello World', backgroundColor:'white' }); var label1 = Titanium.UI.createLabel({ color:'black', textAlign:'center', width: 100 }); label1.text = 'howdy?'; win1.add(label1); win1.open(); tiConf.eu, valencia, 24/02/2013 14
  • 21.
    Terminology var win1 = Titanium.UI.createWindow({ title:'Hello World', creation backgroundColor:'white' properties }); var label1 = Titanium.UI.createLabel({ color:'black', textAlign:'center', width: 100 }); label1.text = 'howdy?'; win1.add(label1); win1.open(); tiConf.eu, valencia, 24/02/2013 14
  • 22.
    Terminology proxy object var win1 = Titanium.UI.createWindow({ title:'Hello World', backgroundColor:'white' }); var label1 = Titanium.UI.createLabel({ color:'black', textAlign:'center', width: 100 }); label1.text = 'howdy?'; win1.add(label1); win1.open(); tiConf.eu, valencia, 24/02/2013 14
  • 23.
    Terminology var win1 = Titanium.UI.createWindow({ title:'Hello World', backgroundColor:'white' }); view proxy var label1 = Titanium.UI.createLabel({ object color:'black', textAlign:'center', width: 100 }); label1.text = 'howdy?'; win1.add(label1); win1.open(); tiConf.eu, valencia, 24/02/2013 14
  • 24.
    Terminology var win1 = Titanium.UI.createWindow({ title:'Hello World', backgroundColor:'white' }); var label1 = Titanium.UI.createLabel({ color:'black', textAlign:'center', width: 100 }); proxy label1.text = 'howdy?'; property win1.add(label1); win1.open(); tiConf.eu, valencia, 24/02/2013 14
  • 25.
    Terminology var win1 = Titanium.UI.createWindow({ title:'Hello World', backgroundColor:'white' }); var label1 = Titanium.UI.createLabel({ color:'black', textAlign:'center', width: 100 }); label1.text = 'howdy?'; win1.add(label1); proxy win1.open(); method tiConf.eu, valencia, 24/02/2013 14
  • 26.
    Proxies & Modules Native View Type Proxy iOS UIView Android View creates manages extends extends Module ViewProxy View has a tiConf.eu, valencia, 24/02/2013 15
  • 27.
    Proxies & Modules Interface State: properties Actions: methods Events: addEventListener(), fireEvent() Native View Type Proxy iOS UIView Android View creates manages extends extends Module ViewProxy View has a tiConf.eu, valencia, 24/02/2013 15
  • 28.
    Proxies & Modules Interface State: properties Actions: methods Events: addEventListener(), fireEvent() Native View Type Proxy iOS UIView Android View creates manages extends extends Module ViewProxy View Methods for the integration within the application lifecycle has a •startup() (iOS) •shutdown() (iOS) •onAppCreate() (Android) tiConf.eu, valencia, 24/02/2013 15
  • 29.
    Proxies & Modules Interface State: properties Actions: methods Events: addEventListener(), fireEvent() Native View Type Proxy iOS UIView Android View creates manages extends extends Module ViewProxy View Additional members for the integration Methods for the integration within the application lifecycle within the UI layout system: •add() has a •startup() (iOS) •remove() •shutdown() (iOS) •height •onAppCreate() (Android) •width •backgroundColor •... tiConf.eu, valencia, 24/02/2013 15
  • 30.
    Let’s create amodule tiConf.eu, valencia, 24/02/2013
  • 31.
    • Create • Develop • Build • Deploy • Debug tiConf.eu, valencia, 24/02/2013 17
  • 32.
    • Create • Develop • Build • Deploy • Debug tiConf.eu, valencia, 24/02/2013 17
  • 33.
  • 34.
    iOS Prerequisites • Same as for Ti app development on iOS: ★ Titanium SDK. ★ Xcode tiConf.eu, valencia, 24/02/2013 19
  • 35.
    Android Prerequisites • Same as for Ti app development on Android: ★ Titanium SDK. ★ Android SDK (+ ANDROID_SDK environment variable) • Additionally: ★ Android NDK (+ ANDROID_NDK environment variable) ★ Ant 1.7.1 (available in PATH) ★ gperf must be installed and in your system PATH. ★ [Eclipse] tiConf.eu, valencia, 24/02/2013 20
  • 36.
    Create (cli) $ alias ti='~/Library/Application Support/Titanium/mobilesdk/osx/3.0.0.GA/ titanium.py' OL SCHO OLD tiConf.eu, valencia, 24/02/2013 21
  • 37.
    Create (cli) $ alias ti='~/Library/Application Support/Titanium/mobilesdk/osx/3.0.0.GA/ titanium.py' $ ti create --type=module --id=ti.conf.sample --name=ticonfsample --platform=iphone --dir=./ios OL SCHO OLD tiConf.eu, valencia, 24/02/2013 21
  • 38.
    Create (cli) $ alias ti='~/Library/Application Support/Titanium/mobilesdk/osx/3.0.0.GA/ titanium.py' $ ti create --type=module --id=ti.conf.sample --name=ticonfsample --platform=iphone --dir=./ios $ ti create --type=module--id=ti.conf.sample --name=ticonfsample --platform=android --dir=./android OL SCHO OLD tiConf.eu, valencia, 24/02/2013 21
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
    Create (Ti Studio) tiConf.eu,valencia, 24/02/2013 23
  • 44.
    Create (Ti Studio) tiConf.eu,valencia, 24/02/2013 24
  • 45.
    Create (Ti Studio) tiConf.eu,valencia, 24/02/2013 25
  • 46.
  • 47.
  • 48.
    Build & Install(cli) $ ./build.py $ unzip -uo ti.conf.sample-iphone-0.1.zip -d ~/Library/Application Support/ Titanium/ tiConf.eu, valencia, 24/02/2013 28
  • 49.
    Build & Install(cli) $ ./build.py $ unzip -uo ti.conf.sample-iphone-0.1.zip -d ~/Library/Application Support/ Titanium/ $ ant $ unzip -uo dist/ti.conf.sample-android-0.1.zip -d ~/Library/Application Support/Titanium/ tiConf.eu, valencia, 24/02/2013 28
  • 50.
    Build (Ti Studio) tiConf.eu,valencia, 24/02/2013 29
  • 51.
    Build & Install(Ti Studio) tiConf.eu, valencia, 24/02/2013 30
  • 52.
    Deploy tiapp.xml <?xml version="1.0" encoding="UTF-8"?> <ti:app xmlns:ti="http://ti.appcelerator.org"> <id>com.omorandi.ticonftest</id> <!-- ... --> <modules> <module platform="iphone">ti.conf.sample</module> <module platform="android">ti.conf.sample</module> </modules> </ti:app> app.js var ticonfsample = require('ti.conf.sample'); Ti.API.info(ticonfsample.example()); Ti.API.info("module exampleProp is => " + ticonfsample.exampleProp); ticonfsample.exampleProp = "This is a test value"; tiConf.eu, valencia, 24/02/2013 31
  • 53.
    Agenda • Methods • Events • Passing • Callbacks arguments • ViewProxies • Returning values • Exceptions • Using properties • Proxies tiConf.eu, valencia, 24/02/2013 32
  • 54.
    Proxy/Module Methods return value -(id)methodName:(id)args { NSString result = @"Hello World”; //do something return result; } tiConf.eu, valencia, 24/02/2013 33
  • 55.
    Proxy/Module Methods return value -(id)methodName:(id)args { NSString result = @"Hello World”; //do something return result; } no return value -(void)methodName:(id)args { //do something } tiConf.eu, valencia, 24/02/2013 33
  • 56.
    Proxy/Module Methods return value return value no args -(id)methodName:(id)args @Kroll.method { public String methodName() NSString result = @"Hello World”; { String result = "Hello world"; //do something //do something return result; } return result; } no return value -(void)methodName:(id)args { //do something } tiConf.eu, valencia, 24/02/2013 33
  • 57.
    Proxy/Module Methods return value return value no args -(id)methodName:(id)args @Kroll.method { public String methodName() NSString result = @"Hello World”; { String result = "Hello world"; //do something //do something return result; } return result; } no return value no return value @Kroll.method -(void)methodName:(id)args public void methodName(String value) { { //do something //do something } } tiConf.eu, valencia, 24/02/2013 33
  • 58.
    Example: xml2json module Expected API var xml2json = require('ti.xml2json'); var json = xml2json.convert(xmlDoc); https://github.com/omorandi/TiXml2Json tiConf.eu, valencia, 24/02/2013 34
  • 59.
    Implementation @implementation TiXml2jsonModule -(NSDictionary) convertXml:(NSString)xmlString { NSDictionary *jsObj; //do conversion stuff return jsObj; } -(id)convert:(id)args { ENSURE_SINGLE_ARG(args, NSString); return [self convertXml:args]; } @end tiConf.eu, valencia, 24/02/2013 35
  • 60.
    Implementation @implementation TiXml2jsonModule -(NSDictionary) convertXml:(NSString)xmlString { NSDictionary *jsObj; //do conversion stuff return jsObj; } -(id)convert:(id)args NSArray of arguments { ENSURE_SINGLE_ARG(args, NSString); return [self convertXml:args]; } @end tiConf.eu, valencia, 24/02/2013 35
  • 61.
    Implementation @implementation TiXml2jsonModule -(NSDictionary) convertXml:(NSString)xmlString { NSDictionary *jsObj; //do conversion stuff return jsObj; } TiBase.h -(id)convert:(id)args NSArray of arguments #define ENSURE_SINGLE_ARG(x,t) { { ENSURE_SINGLE_ARG(args, NSString); x = (t*)[x objectAtIndex:0]; } return [self convertXml:args]; } @end tiConf.eu, valencia, 24/02/2013 35
  • 62.
    Utility Macros TiBase.h #define ENSURE_CLASS(x,t) #define ENSURE_CLASS_OR_NIL(x,t) #define ENSURE_TYPE(x,t) #define ENSURE_TYPE_OR_NIL(x,t) #define ENSURE_ARG_COUNT(x,c) #define ENSURE_SINGLE_ARG(x,t) #define ENSURE_SINGLE_ARG_OR_NIL(x,t) #define ENSURE_DICT(x) #define ENSURE_ARRAY(x) #define ENSURE_STRING(x) #define THROW_INVALID_ARG(m) ... tiConf.eu, valencia, 24/02/2013 36
  • 63.
    Types Supported Directly Conversion Utilities NSString NSDictionary #import "TiUtils.h" NSArray CGFloat f = [TiUtils floatValue:arg]; NSInteger f = [TiUtils intValue:arg]; NSNumber NSString *value = [TiUtils stringValue:arg]; NSDate TiColor *bgcolor = [TiUtils colorValue:arg]; NSNull UIColor *backgroundColor = [bgcolor color]; TiProxy tiConf.eu, valencia, 24/02/2013 37
  • 64.
    Return Values • Single Value (NSString, NSNumber, …) • Collections (NSArray) ★ Converted into a JS Array object • Dictionary (NSDictionary) ★ Converted into a JS object ★ key->value ===> property->value • Proxy (TiProxy) tiConf.eu, valencia, 24/02/2013 38
  • 65.
    Return Values • Single Value (NSString, NSNumber, …) • Collections (NSArray) ★ Converted into a JS Array object • Dictionary (NSDictionary) ★ Converted into a JS object TS BJEC ★ key->value ===> property->value DO SE EA EL OR • Proxy (TiProxy) UR NA UT RET tiConf.eu, valencia, 24/02/2013 38
  • 66.
    xml2json Android import org.appcelerator.kroll.KrollDict; @Kroll.module(name="Tixml2json", id="ti.xml2json") public class Tixml2jsonModule extends KrollModule { @Kroll.method public KrollDict convert(String xml) { KrollDict json = null; //do conversion stuff return json; } } tiConf.eu, valencia, 24/02/2013 39
  • 67.
    xml2json Android import org.appcelerator.kroll.KrollDict; @Kroll.module(name="Tixml2json", id="ti.xml2json") public class Tixml2jsonModule extends KrollModule { KrollDict.java @Kroll.method public class KrollDict extends HashMap<String, Object> public KrollDict convert(String xml) { KrollDict json = null; //do conversion stuff return json; } } tiConf.eu, valencia, 24/02/2013 39
  • 68.
    Types Supported Directly Conversion Utilities String int import org.appcelerator.titanium.util.TiConvert; float int val = TiConvert.toInt(obj); double float val = TiConvert.toFloat(obj); boolean double val = TiConvert.toDouble(obj); boolean val = TiConvert.toBoolean(obj); Object[] int color = TiConvert.toColor(str); HashMap<String, Object> ... TiProxy http://builds.appcelerator.com.s3.amazonaws.com/module-apidoc/2.0.0/android/org/appcelerator/titanium/util/TiConvert.html tiConf.eu, valencia, 24/02/2013 40
  • 69.
    Return Values • Single Value (String, Integer, …) • Collections (Object[]) ★ Converted into a JS Array object • Dictionary (HashMap<String, Object>) ★ Converted into a JS object ★ key->value ===> property->value • Proxy (TiProxy) tiConf.eu, valencia, 24/02/2013 41
  • 70.
    Polymorphic Methods -(id)convert:(id)args { ENSURE_ARG_COUNT(args, 1); id arg = [args objectAtIndex:0]; if ([arg isKindOfClass:[NSString class]]) { return [self convertFromString:arg]; } else if ([arg isKindOfClass:[TiBlob class]]) { return [self convertFromData:arg]; } else { [self throwException:@"Expected blob or string argument" subreason:nil location:CODELOCATION]; } } tiConf.eu, valencia, 24/02/2013 42
  • 71.
    Polymorphic Methods public KrollDict convertFromString(String xml); public KrollDict convertFromBlob(TiBlob blob) @Kroll.method public String convert(Object arg) { if (arg instanceof String) { return "string"; } if (arg instanceof TiBlob) { return "blob"; } throw new IllegalArgumentException("Invalid argument type, expected blob or string"); } tiConf.eu, valencia, 24/02/2013 43
  • 72.
    Varargs -(void) varArgsMethod:(id)args { for (int i = 0; i < [args count]; i++) { id arg = [args objectAtIndex:i]; // do something with arg } } tiConf.eu, valencia, 24/02/2013 44
  • 73.
    Varargs @Kroll.method public void varArgsMethod(Object[] args) { for (int i = 0; i < args.length; i++) { Object arg = args[i]; // do something with arg } } tiConf.eu, valencia, 24/02/2013 45
  • 74.
    Properties set module.propertyName = "HELLO"; get Ti.API.info("Property: " + module.propertyName); tiConf.eu, valencia, 24/02/2013 46
  • 75.
    Properties TiMyModule.h @interface TiMyModule: TiModule @property (nonatomic, readwrite, retain) NSString* propertyName; @end TiMyModule.m @implementation TiMyModule: TiModule @synthesize propertyName; @end tiConf.eu, valencia, 24/02/2013 47
  • 76.
    Properties: Setter/Getter - (void) setPropertyName:(id)args { // set property and do stuff } - (id) propertyName { // do something and return value; } tiConf.eu, valencia, 24/02/2013 48
  • 77.
    Properties: Setter/Getter @Kroll.module(name="My", id="ti.my") public class MyModule extends KrollModule { private String propertyName; @Kroll.getProperty @Kroll.method public String getPropertyName() { return propertyName; } @Kroll.setProperty @Kroll.method public void setPropertyName(String value) { propertyName = value; } } tiConf.eu, valencia, 24/02/2013 49
  • 78.
    Constants var smsModule = require('ti.ios.sms'); function sendCallback(e){ switch (e.result) { case sms.SENT: result = 'SENT'; break; case sms.FAILED: result = 'FAILED'; break; case sms.CANCELLED: result = 'CANCELLED'; break; } Ti.API.info("Property: " + module.propertyName); } tiConf.eu, valencia, 24/02/2013 50
  • 79.
    Constants https://github.com/omorandi/TiSMSDialog/blob/master/Classes/ComOmorandiSMSDialogProxy.m //create the accessor methods for the SENT, CANCELLED and FAILED constants MAKE_SYSTEM_PROP(SENT,MessageComposeResultSent); MAKE_SYSTEM_PROP(CANCELLED,MessageComposeResultCancelled); MAKE_SYSTEM_PROP(FAILED,MessageComposeResultFailed); tiConf.eu, valencia, 24/02/2013 51
  • 80.
    Constants https://github.com/omorandi/TiSMSDialog/blob/master/Classes/ComOmorandiSMSDialogProxy.m //create the accessor methods for the SENT, CANCELLED and FAILED constants MAKE_SYSTEM_PROP(SENT,MessageComposeResultSent); MAKE_SYSTEM_PROP(CANCELLED,MessageComposeResultCancelled); MAKE_SYSTEM_PROP(FAILED,MessageComposeResultFailed); TiBase.h #define MAKE_SYSTEM_PROP(name,map) -(NSNumber*)name { return [NSNumber numberWithInt:map]; } tiConf.eu, valencia, 24/02/2013 51
  • 81.
    Constants https://github.com/omorandi/TiSMSDialog/blob/master/Classes/ComOmorandiSMSDialogProxy.m //create the accessor methods for the SENT, CANCELLED and FAILED constants MAKE_SYSTEM_PROP(SENT,MessageComposeResultSent); MAKE_SYSTEM_PROP(CANCELLED,MessageComposeResultCancelled); MAKE_SYSTEM_PROP(FAILED,MessageComposeResultFailed); TiBase.h #define MAKE_SYSTEM_PROP(name,map) -(NSNumber*)name { return [NSNumber numberWithInt:map]; } @Kroll.module(name="Sms", id="ti.android.sms") public class SmsModule extends KrollModule { @Kroll.constant public static final int SENT = 0; @Kroll.constant public static final int CANCELLED = -1; @Kroll.constant public static final int FAILED = -2; } tiConf.eu, valencia, 24/02/2013 51
  • 82.
    Proxy Objects var smsModule = require('ti.ios.sms'); //create the smsDialog object var smsDialog = smsModule.createSMSDialog({ recipients: ['+123456789'], messageBody: 'hello' }); smsDialog.open(); tiConf.eu, valencia, 24/02/2013 52
  • 83.
    Creating a Proxy TiIosSmsSMSDialogProxy.h @interface TiIosSmsSMSDialogProxy: TiProxy<MFMessageComposeViewControllerDelegate> @end TiIosSmsSMSDialogProxy.m @implementation TiIosSmsSMSDialogProxy - (void)open:(id)args { // retrieve properties (either set on creation, or later) NSArray * recipients = [self valueForUndefinedKey:@"recipients"]; NSString * messageBody= [TiUtils stringValue:[self valueForUndefinedKey:@"messageBody"]]; // do stuff } @end tiConf.eu, valencia, 24/02/2013 53
  • 84.
    Proxy Objects var smsModule = require('ti.android.sms'); //create the sms object var sms = smsModule.createSms({ recipient: '+123456789', messageBody: 'hello' }); sms.send(); tiConf.eu, valencia, 24/02/2013 54
  • 85.
    Creating a Proxy @Kroll.proxy(creatableInModule=SmsModule.class) public class SmsProxy extends KrollProxy { private String messageBody = null; private String recipient = null; // Constructor public SmsProxy() { super(); } // Handle creation options @Override public void handleCreationDict(KrollDict options) { super.handleCreationDict(options); if (options.containsKey("messageBody")) { messageBody = (String)options.get("messageBody"); } if (options.containsKey("recipient")) { recipient = (String)options.get("recipient"); } } @Kroll.method public void send() { // send the message } } tiConf.eu, valencia, 24/02/2013 55
  • 86.
    Events Notify a change of state, or an asynchronous event // create the Module object var tibarcode = require('ti.barcode'); var scanner = tibarcode.createScanner(); // success event listener scanner.addEventListener('success', function(e) { var code = e.barcode; var type = e.type; alert('Found code: ' + code + ' type: ' + type); }); tiConf.eu, valencia, 24/02/2013 56
  • 87.
    Events @implementation TiBarcodeScannerProxy // Scanner Delegate - (void) imagePickerController: (UIImagePickerController*)reader didFinishPickingMediaWithCode:(NSString*)code andType:(NSString*)type { if ([self _hasListeners:@"success"]){ NSDictionary *results = [NSDictionary dictionaryWithObjectsAndKeys: code, @"code", type, @"type", nil]; [self fireEvent:@"success" withObject:results]; } } @end tiConf.eu, valencia, 24/02/2013 57
  • 88.
    Events public class ScannerProxy extends KrollProxy { void onScannerResult(String code, String type) { if (hasListeners("success")) { KrollDict event = new KrollDict(); event.put("code", code); event.put("type", type); fireEvent("success", event); } } } tiConf.eu, valencia, 24/02/2013 58
  • 89.
    Callbacks Notify the result of an asynchronous action // let's not freeze on huge xml data xml2json.convertAsync(xmlDoc, function(data) { Ti.API.info("JSON object: " + JSON.stringify(data.json)); }); tiConf.eu, valencia, 24/02/2013 59
  • 90.
    Callbacks -(void)convertAsync:(id)args { ENSURE_ARG_COUNT(args, 2); id xml = [args objectAtIndex:0]; KrollCallback *cb = [args objectAtIndex:1]; ENSURE_TYPE(cb, KrollCallback); //pass just the first (string|blob) arg to convertXml() dispatch_async(dispatchQueue, ^(void) { id result = [self convertXml:xml]; NSDictionary *cbArgs = [NSDictionary dictionaryWithObject:result forKey:@"json"]; [self _fireEventToListener:@"success" withObject:cbArgs listener:cb thisObject:nil]; }); } tiConf.eu, valencia, 24/02/2013 60
  • 91.
    Callbacks @Kroll.method public KrollDict convertAsync(String xml, final KrollFunction callback) { new Thread() { @Override public void run() { KrollDict json = null; //do conversion stuff KrollDict data = new KrollDict(); event.put("json", json); callback.call(getKrollObject(), data); } }.start(); } tiConf.eu, valencia, 24/02/2013 61
  • 92.
    ViewProxy Manages the Holds the state native view of a view hierarchy Methods Native Views ViewProxy Hierarchy Properties View (get/set) Events tiConf.eu, valencia, 24/02/2013 62
  • 93.
    ViewProxy Manages the Holds the state native view of a view hierarchy Methods Native Views ViewProxy Hierarchy Properties View (get/set) Events JS THREAD tiConf.eu, valencia, 24/02/2013 62
  • 94.
    ViewProxy Manages the Holds the state native view of a view hierarchy Methods Native Views ViewProxy Hierarchy Properties View (get/set) Events JS THREAD UI THREAD tiConf.eu, valencia, 24/02/2013 62
  • 95.
    ViewProxy Manages the Holds the state native view of a view hierarchy Methods Native Views ViewProxy Hierarchy Properties View (get/set) Events JS THREAD UI THREAD Mostly async tiConf.eu, valencia, 24/02/2013 62
  • 96.
    Super-Smooth TableView • API • createMessagesView(properties); • setMessages([]messages); • insert(message); • addEventListener(‘click’, callback); tiConf.eu, valencia, 24/02/2013 63
  • 97.
    Implementation MessagesViewProxy TiSmoothMessagesViewProxy.h @interface TiSmoothMessagesViewProxy : TiViewProxy @property (nonatomic, retain) MessagesCollection *msgs; //model @end TiSmoothMessagesViewProxy.m @implementation TiSmoothMessagesViewProxy @synthesize msgs; //other methods -(void) insert:(id)args { ENSURE_SINGLE_ARG(args, NSDictionary); [self.msgs insertMessageOnTop:[self messageFromDictionary:(NSDictionary*)args]]; [self makeViewPerformSelector:@selector(addMessage:) withObject:args createIfNeeded:YES waitUntilDone:NO]; } @end tiConf.eu, valencia, 24/02/2013 64
  • 98.
    Implementation MessagesViewProxy TiSmoothMessagesViewProxy.h @interface TiSmoothMessagesViewProxy : TiViewProxy @property (nonatomic, retain) MessagesCollection *msgs; //model @end TiSmoothMessagesViewProxy.m @implementation TiSmoothMessagesViewProxy @synthesize msgs; //other methods -(void) insert:(id)args { ENSURE_SINGLE_ARG(args, NSDictionary); [self.msgs insertMessageOnTop:[self messageFromDictionary:(NSDictionary*)args]]; create the view and call addMessage on the UI thread [self makeViewPerformSelector:@selector(addMessage:) withObject:args createIfNeeded:YES waitUntilDone:NO]; } @end tiConf.eu, valencia, 24/02/2013 64
  • 99.
    Implementation MessagesView TiSmoothMessagesView.h @interface TiSmoothMessagesView : TiUIView { MessagesViewController *viewController; } @end TiSmoothMessagesView.m @implementation TiSmoothMessagesView -(void)initializeState { [super initializeState]; viewController = [[InboxViewController alloc] initWithStyle:UITableViewStylePlain]; UITableView *tableView = viewController.tableView; [self addSubview:tableView]; } -(void)addMessage:(InboxMessage*)message { ENSURE_UI_THREAD(addMessage, message); [viewController addMessageOnTop:message]; } -(void)frameSizeChanged:(CGRect)frame bounds:(CGRect)bounds { [TiUtils setView: viewController.tableView positionRect:bounds]; } @end tiConf.eu, valencia, 24/02/2013 65
  • 100.
    Implementation MessagesView TiSmoothMessagesView.h @interface TiSmoothMessagesView : TiUIView { MessagesViewController *viewController; } @end TiSmoothMessagesView.m @implementation TiSmoothMessagesView -(void)initializeState called by Titanium at view creation { [super initializeState]; viewController = [[InboxViewController alloc] initWithStyle:UITableViewStylePlain]; UITableView *tableView = viewController.tableView; [self addSubview:tableView]; } -(void)addMessage:(InboxMessage*)message { ENSURE_UI_THREAD(addMessage, message); [viewController addMessageOnTop:message]; } -(void)frameSizeChanged:(CGRect)frame bounds:(CGRect)bounds { [TiUtils setView: viewController.tableView positionRect:bounds]; } @end tiConf.eu, valencia, 24/02/2013 65
  • 101.
    Implementation MessagesView TiSmoothMessagesView.h @interface TiSmoothMessagesView : TiUIView { MessagesViewController *viewController; } @end TiSmoothMessagesView.m @implementation TiSmoothMessagesView -(void)initializeState called by Titanium at view creation { [super initializeState]; viewController = [[InboxViewController alloc] initWithStyle:UITableViewStylePlain]; UITableView *tableView = viewController.tableView; [self addSubview:tableView]; } -(void)addMessage:(InboxMessage*)message { ENSURE_UI_THREAD(addMessage, message); [viewController addMessageOnTop:message]; } -(void)frameSizeChanged:(CGRect)frame bounds:(CGRect)bounds called by Titanium for notifying a { change in frame size [TiUtils setView: viewController.tableView positionRect:bounds]; } @end tiConf.eu, valencia, 24/02/2013 65
  • 102.
    module.xcconfig // // PLACE ANY BUILD DEFINITIONS IN THIS FILE AND THEY WILL BE // PICKED UP DURING THE APP BUILD FOR YOUR MODULE // OTHER_LDFLAGS=$(inherited) -framework AVFoundation -framework CoreMedia -framework CoreVideo -framework QuartzCore /usr/lib/libiconv.dylib build & package Module Package (.zip) app bundle tiConf.eu, valencia, 24/02/2013 66
  • 103.
    module.xcconfig // // PLACE ANY BUILD DEFINITIONS IN THIS FILE AND THEY WILL BE // PICKED UP DURING THE APP BUILD FOR YOUR MODULE // OTHER_LDFLAGS=$(inherited) -framework AVFoundation -framework CoreMedia -framework CoreVideo -framework QuartzCore /usr/lib/libiconv.dylib build & package Module Package (.zip) app bundle tiConf.eu, valencia, 24/02/2013 66
  • 104.
    timodule.xml <?xml version="1.0" encoding="UTF-8"?> <ti:module xmlns:ti="http://ti.appcelerator.org" xmlns:android="http://schemas.android.com/apk/res/android"> <iphone> </iphone> <android xmlns:android="http://schemas.android.com/apk/res/android"> <manifest> <uses-permission android:name="android.permission.SEND_SMS" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application> <activity android:name="ti.conf.sample.MyCustomActivity"> </activity> </application> </manifest> </android> <mobileweb> </mobileweb> </ti:module> build & package AndroidManifest.xml Module Package (.zip) app.apk tiConf.eu, valencia, 24/02/2013 67
  • 105.
  • 106.
    Create a debugbuild $ sed s/Release/Debug/ build.py > build_debug.py build.py 168: rc = os.system("xcodebuild -sdk iphoneos -configuration Release") 171: rc = os.system("xcodebuild -sdk iphonesimulator -configuration Release") build_debug.py 168: rc = os.system("xcodebuild -sdk iphoneos -configuration Debug") 171: rc = os.system("xcodebuild -sdk iphonesimulator -configuration Debug") tiConf.eu, valencia, 24/02/2013 69
  • 107.
  • 108.
    Debug Logs @Kroll.module(name="Ticonfsample", id="ti.conf.sample") public class TiconfsampleModule extends KrollModule { // Tag for debug log messages private static final String LCAT = "TiconfsampleModule"; // tells if debug logging has been enabled in the Titanium application private static final boolean DBG = TiConfig.LOGD; @Kroll.method public void doSomething() { Log.d(LCAT, "doing something"); } } tiConf.eu, valencia, 24/02/2013 71
  • 109.
  • 110.