Slide 1

Slide 1 text

TiConf Amsterdam - Xavier Lacot - June 29th, 2014 Everything Titanium using the CLI

Slide 2

Slide 2 text

Hello, I am Xavier • Founder, Web- and Mobile- expert at JoliCode • Contributor to several Open Source projects • Titanium developer since 2009 • Speaker at CodeStrong and TiConf • Former President of the French Association of PHP Users • Co-organizer of the Titanium Paris meetup group @xavierlacot with TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 2

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

A (brief) history as a Titanium developer...

Slide 5

Slide 5 text

Titanium developer: 2009-2011 TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 5

Slide 6

Slide 6 text

Titanium Studio: 2011-present TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 6

Slide 7

Slide 7 text

My experience aside Titanium • Symfony developer since a lot of years • nodejs developer more recently • used to use Command Line Interfaces, aka. CLI TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 7

Slide 8

Slide 8 text

The day when Titanium got his very own CLI TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 8

Slide 9

Slide 9 text

CLI > GUI • a long debate... • Pros: • higher productivity • automate/script tasks • use the convenient tools rather than a "all-in-one" solution • My CLI is 1000% faster than your Eclipse crap ( Appcelerator) • feel like a hipster-hacker when it works • Cons: not polished and well packaged TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 9

Slide 10

Slide 10 text

GUI CLI TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 10

Slide 11

Slide 11 text

The Titanium CLI...

Slide 12

Slide 12 text

Get / install the tool • Installing the CLI is an easy task: $  npm  install  -­‐g  titanium • Want to live dangerously? $  npm  install  -­‐g  git://github.com/appcelerator/titanium.git TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 12

Slide 13

Slide 13 text

ticonf-2014$ ^[p $ titanium sdk • $ ti sdk helps manage the installed Titanium sdk: • list : list installed sdks • install : (un-)install a sdk version / update to newest • select : select a specific version by default TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 13

Slide 14

Slide 14 text

$ titanium config • ti config : list all options • ti config user.email : retrieves the value of a property • ti config user.email "[email protected]" : sets property Some interesting configuration options • cli.completion : enable cli tab-completion (working soon) • genymotion.enabled : enabled to push directly to genymotion without manually calling "adb" TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 14

Slide 15

Slide 15 text

These settings are stored in ~/.titanium/config.json {   "user":  {     "name":  "Xavier  Lacot",     "email":  "[email protected]"   },   "app":  {     "workspace":  "/Users/xavier/Documents/workspace/titanium",     "idprefix":  "com.jolicode",   },   [...],   "sdk":  {     "selected":  "3.2.3.GA"   },   "genymotion":  {     "enabled":  true   } } TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 15

Slide 16

Slide 16 text

ticonf-2014$ ^[p $ titanium create • A Swiss tool for creating projects! • interactive: asks common questions (id, name, platforms, etc.) • fast: create a project in 10 seconds! • lightweight: it uses a simple two-tabs project template TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 16

Slide 17

Slide 17 text

TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 17

Slide 18

Slide 18 text

Troubleshouting the configuration • $ ti setup check : checks the configuration TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 18

Slide 19

Slide 19 text

Troubleshouting the configuration • $ ti setup quick : complete setup (developer name, sdk version, etc.) • $ ti setup app : default app values (company app id prefix, publisher, website, etc.) • other commands: user , network , cli , sdk , ios , android TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 19

Slide 20

Slide 20 text

ticonf-2014$ ^[p $ titanium build • all platforms are supported • simulators or real devices • even store-submission is possible! $  ti  build  -­‐p  ios $  ti  build  -­‐p  android  -­‐T  dist-­‐playstore  -­‐K  jolicode.keystore  -­‐L  jolicode TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 20

Slide 21

Slide 21 text

A lot of other commands... • $ ti status displays the current login/user status • $ ti info displays a complete diagnosis of the platform: • OS, JAVA, XCode • Titanium SDKs • Android SDK, Platforms and Emulators • Genymotion Emulators • iOS certificates and provisionnings, simulators, connected devices • etc. TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 21

Slide 22

Slide 22 text

Extending the CLI

Slide 23

Slide 23 text

Built extensible through plugins • add new commands • hook into exisiting commands TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 23

Slide 24

Slide 24 text

Creating new commands 1. a single js file, where you want /path/to/sthing/pony.js 2. register it for Titanium's cli: $  ti  config  -­‐a  paths.commands  /path/to/sthing (adds /path/to/sthing to the paths.commands config array) 3. write the content of the command • a declarative part • command description, name, etc. • optionnaly, a config() method to set the default value of some execution option TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 24

Slide 25

Slide 25 text

Creating new commands • the content of the command • a validate() method called to validate the options passed to the command • a run() method, the "body" of the command • the command name is the filename • pony for /path/to/sthing/pony.js • not possible to rename/alias it - ticket + PR on their way :-) • the latter loaded takes precedence the last pony command overrides an earlier pony TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 25

Slide 26

Slide 26 text

A sample custom command exports.cliVersion  =  '>=3.2'; exports.name  =  'pony:fly'; exports.desc  =  'makes  ponies  fly'; exports.config  =  function(logger,  config,  cli)  {        return  {                noAuth:  true,                skipBanner:  true,                options:  {                        quantity:  {                                abbr:  'qty',                                default:  1,                                desc:  'number  of  ponies  flying'                        }                }        }; };  ​  ​  ​ 1 3 TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 26

Slide 27

Slide 27 text

A sample custom command exports.cliVersion  =  '>=3.2'; exports.name  =  'pony:fly'; exports.desc  =  'makes  ponies  fly'; exports.config  =  function(logger,  config,  cli)  {        return  {                noAuth:  true,                skipBanner:  true,                options:  {                        quantity:  {                                abbr:  'qty',                                default:  1,                                desc:  'number  of  ponies  flying'                        }                }        }; };  ​  ​  ​  ​  ​  ​  ​  ​  ​  ​  ​  ​  ​ 5 17 TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 27

Slide 28

Slide 28 text

A sample custom command exports.validate  =  function  (logger,  config,  cli)  {        if  (cli.argv.quantity)  {                if  (!/^\d+$/.test(cli.argv.quantity))  {                        logger.banner();                        logger.error('quantity  must  be  an  integer\n');                        process.exit(1);                }        } }; exports.run  =  function  (logger,  config,  cli,  finished)  {        var  i  =  0;        while  (i  <  cli.argv.quantity)  {                console.log('ponies  are  flying!');                i++;        } };  ​  ​  ​  ​  ​  ​  ​  ​  ​ 1 9 TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 28

Slide 29

Slide 29 text

A sample custom command exports.validate  =  function  (logger,  config,  cli)  {        if  (cli.argv.quantity)  {                if  (!/^\d+$/.test(cli.argv.quantity))  {                        logger.banner();                        logger.error('quantity  must  be  an  integer\n');                        process.exit(1);                }        } }; exports.run  =  function  (logger,  config,  cli,  finished)  {        var  i  =  0;        while  (i  <  cli.argv.quantity)  {                console.log('ponies  are  flying!');                i++;        } };  ​  ​  ​  ​  ​  ​  ​  ​ 11 18 TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 29

Slide 30

Slide 30 text

ticonf-2014$ ^[p A sample custom command TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 30

Slide 31

Slide 31 text

Hooking into commands: concept • idea: run operations when certain events occur • hooks are a common concept, not specific to Titanium • can somehow be seen as "events" at the cli scale TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 31

Slide 32

Slide 32 text

Hooking into commands: howto 1. a single js file, where you want /path/to/sthing/soundAfterBuild.js 2. register it for Titanium's cli: $  ti  config  -­‐a  paths.hooks  /path/to/sthing (adds /path/to/sthing to the paths.hooks config array) 3. write the content of the hook • using cli.on() , attach listeners to the occurence of hooks • cli.addHook() is an alias for cli.on() TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 32

Slide 33

Slide 33 text

ticonf-2014$ ^[p A sample custom hook var  path  =  require('path'),   play  =  require('play'); exports.cliVersion  =  '>=3.2'; exports.init  =  function(logger,  config,  cli,  nodeappc)  {        cli.on('build.post.compile',  function(builder,  next)  {                play.sound(path.resolve(__dirname,  'sounds',  'trumpet.wav'));                next();        }) };  ​ 6 TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 33

Slide 34

Slide 34 text

• build.pre.construct • build.pre.compile • build.android.startEmulator • build.android.copyResource • build.android.titaniumprep • build.android.writeAndroidManifest • build.android.aapt • build.android.javac • build.android.proguard • build.android.dexer • build.android.jarsigner • build.android.zipalign • build.ios.copyResource • build.ios.prerouting • build.ios.titaniumprep • build.ios.xcodebuild • build.ios.writeBuildManifest • build.post.compile • build.finalize • create.pre • create.post • clean.pre • clean.post • cli:go • cli:command-loaded • cli:pre-validate • cli:post-validate • cli:pre-execute • cli:post-execute • help:header Available hooks TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 34

Slide 35

Slide 35 text

Some remarks about hooks • you can call hooks manually, using cli.emit : • hooks callbacks can have a priority (default 1000): cli.on('some.event',  {        priority:  999,        post:  function()  {  /*  this  is  the  callback  */  } }); The lower the priority, the earlier the hook is executed /**  *  @param  {String|Array}  hookNames  -­‐  The  hook  name  or  an  array  of  many  hook  names  *  @param  {Object}  [data]  -­‐  The  event  payload  *  @param  {Function}  callback  -­‐  A  callback  when  the  event  has  finished  firing  */ function  emit(hookNames,  data,  callback) TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 35

Slide 36

Slide 36 text

Some remarks about commands • do not forget to manually call the finished() callback: Else, the cli:post-execute hooks never gets called exports.run  =  function  (logger,  config,  cli,  finished)  {        var  i  =  0;        while  (i  <  cli.argv.quantity)  {                console.log('ponies  are  flying!');                i++;        }        if  (typeof  finished  ==  'function')  {                finished()        } };  ​  ​  ​ 9 11 TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 36

Slide 37

Slide 37 text

package cli plugins • cli plugins are made of hooks and commands, and can be installed globally or locally • global install: • create the plugin somewhere, eg. /path/to/plugin • $ ti config paths.plugins -a /path/to/plugin • the plugin can contain commands and/or hooks • it will work in every Titanium project TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 37

Slide 38

Slide 38 text

package cli plugins • local install: • put the plugin in the plugins folder of your project • reference it in tiapp.xml : < app   ti="http://ti.appcelerator.org">                        ticonf.playSound         • may only contain hooks, not commands • usable hooks: the ones after cli:post-validate ti: xmlns: ti: TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 38

Slide 39

Slide 39 text

Some interesting CLI plugins

Slide 40

Slide 40 text

ti-i18n • a replacement for alloy's extract-i18n command • extract translation strings from your app $  ti  help  i18n Usage:  titanium  i18n   CLI  to  manage  internationalizing  your  Titanium  app. Titanium  i18n  CLI  Subcommands:      extract      extract  i18n  strings  from  the  source  code  (js  and  tss  files) • focus on your code, not on translation dictionnaries thanks TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 40

Slide 41

Slide 41 text

ti-installr-hook • push your dev builds to Installr • install it: $  npm  install  -­‐g  ti-­‐installr-­‐hook  -­‐-­‐unsafe-­‐perm • Set token in tiapp.xml : • build the app: $  ti  build  -­‐p  ios  -­‐T  dist-­‐adhoc  -­‐-­‐installr • and voila! ENTER_INSTALLR_API_TOKEN_HEREtrue TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 41

Slide 42

Slide 42 text

ti-testflight-hook • push your dev builds to Testflight (ios only). Do not use it. Go Installr. • install it: $  npm  install  -­‐g  ti-­‐testflight-­‐hook  -­‐-­‐unsafe-­‐perm • Set token in tiapp.xml : ENTER_API_TOKEN_HERE ENTER_TEAM_TOKEN_HERE • build the app: $  ti  build  -­‐p  ios  -­‐T  dist-­‐adhoc  -­‐-­‐testflight • and voila! thanks TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 42

Slide 43

Slide 43 text

alloy hooks into ti cli the $ alloy new task installs this plugin: exports.installPlugin  =  function(alloyPath,  projectPath)  {   var  id  =  'ti.alloy';   //  copy  plugin   var  srcFile  =  path.join(alloyPath,'Alloy','plugin',CONST.PLUGIN_FILE);   var  destFile  =  path.join(projectPath,'plugins',id,CONST.PLUGIN_FILE);   exports.copyFileSync(srcFile,  destFile);   //  add  the  plugin  to  tiapp.xml,  if  necessary   tiapp.init(path.join(projectPath,  'tiapp.xml'));   tiapp.installPlugin({     id:  'ti.alloy',     version:  '1.0'   }); }; thanks TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 43

Slide 44

Slide 44 text

TiNy CLI Some magic for lazy developers annoyed with long command lines 1. install it: $  npm  install  -­‐g  tn  -­‐-­‐unsafe-­‐perm 2. Have fun: $  titanium  build  -­‐-­‐platform  ios  -­‐-­‐device-­‐family  ipad now types: $  tn  ipad TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 44

Slide 45

Slide 45 text

TiNy CLI Stunning features: • save build receipes: now types: $  tn  ship-­‐my-­‐app • lots of built-in receipes (pre-saved configurations). List: $  tn  list $  tn  save  ship-­‐my-­‐app  172B24F5-­‐1337-­‐1337-­‐1337-­‐D08EB0A7EA5D  "Check  Norris  (6MBV5WT2BD)" thanks TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 45

Slide 46

Slide 46 text

Other Titanium-related CLI

Slide 47

Slide 47 text

ticonf-2014$ ^[p $ alloy • all alloy developers know the alloy CLI tool • new : turns a "classic" project into "alloy" style • compile : runs the alloy compilation • generate : generates code (controllers, widgets, etc.) TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 47

Slide 48

Slide 48 text

$ gittio TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 48

Slide 49

Slide 49 text

$ gittio • also available as a CLI tool! $  npm  install  -­‐g  gittio • manage native modules and alloy widgets using gitt.io: $  gittio  install  com.jolicode.pageflow $  gittio  update  -­‐g $  gittio  update  -­‐t  widget • want to try a module / widget? $  gittio  demo  dk.napp.drawer  -­‐p  ios thanks TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 49

Slide 50

Slide 50 text

tiapp.xml • not a CLI tool • a library for parsing and manipulating the tiapp.xml file npm  install  tiapp.xml • Usage: var  tiapp  =  require('tiapp.xml').load('./tiapp.xml'); tiapp.id  =  'com.other.name'; //  or  whichever  of  tiapp.xml  properties tiapp.write(); thanks TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 50

Slide 51

Slide 51 text

$ ticons • 2 versions of TiCons: • http://ticons.fokkezb.nl/ (PHP implementation) • The CLI tool ✌ $  npm  install  -­‐g  ticons Note that it has a dependacy on imagemagick . • Supports: • icons and assets • splashscreen - with 9-patch and locale support • more up-to-date than Appcelerator's alternative Spork (outdated) TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 51

Slide 52

Slide 52 text

$ ticons • icons radius for android • orientation, platform and project type detection • mutualization of ios and Android MDPI images (lighter repository) $  ticons  -­‐h    Usage:  ticons  command    [options]    Commands:        icons  [options]  [input]  generate  icons        splashes  [options]  [input]  generate  splash  screens  (aka  launch  images)        assets  [input]                  generate  missing  densities  for  input  asset(s) thanks TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 52

Slide 53

Slide 53 text

$ tipi (and tetanize) • tetanize : • a "npm package" to "titanium commonjs module" transformer $  npm  install  -­‐g  tetanize • Usage: $  cd  /path/to/underscorejs $  tetanize • tipi : • keeps track of tetanize -generated modules installed $  npm  install  -­‐g  tipi $  tipi  install  underscore thanks TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 53

Slide 54

Slide 54 text

$ tishadow • no, promises, I won't say a word about TiShadow. Go use it now. thanks TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 54

Slide 55

Slide 55 text

$ ti-stealth • avoid to ship applications with console.log statements $  npm  install  -­‐g  ti-­‐stealth • remove or restore debug statements from the CLI: $  ti-­‐stealth  enable  [-­‐-­‐levels  ]  [-­‐-­‐not-­‐levels  ] $  ti-­‐stealth  restore comments / uncomments console.log and Ti.Api.info|debug|error|etc. in Resources • a sample alloy.jmk file shows how to automate logs removal in production and... guess what... thanks TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 55

Slide 56

Slide 56 text

Xavier Lacot http://jolicode.com Thank you! Go Oranje!