Extending Titanium with native iOS and Android modules

Post on 09-May-2015

36.633 views 4 download

description

This is the presentation used for the workshop on Titanium module development held at tiConf 2013 in Valencia

Transcript of Extending Titanium with native iOS and Android modules

tiConf.eu, valencia, 24/02/2013

Native iOS & Android Modules

Extending Titanium

tiConf.eu, valencia, 24/02/2013

Olivier MorandiSoftware en!ineer

http://titaniumninja.comolivier.morandi@gmail.com@olivier_morandihttps://github.com/omorandi

2

tiConf.eu, valencia, 24/02/2013

Module development is so

2010

tiConf.eu, valencia, 24/02/2013

Why Bother?

• To levera!e native features★ Underlyin! OS★ 3rd party libraries

• Performance★ To optimize the User Experience

4

tiConf.eu, valencia, 24/02/2013

Learnin! Resources

tiConf.eu, valencia, 24/02/2013 7

tiConf.eu, valencia, 24/02/2013

• Titanium Mobile SDK★ https://!ithub.com/appcelerator/

titanium_mobile

• Example modules from Appcelerator★ https://!ithub.com/appcelerator/

titanium_modules

8

Source code

tiConf.eu, valencia, 24/02/2013

Follow these guys

• Aaron K. Saunders: https://!ithub.com/aaronksaunders

• Ben Bahrenbur!: https://!ithub.com/benbahrenbur!

• Christian Sullivan: https://!ithub.com/euforic

• David Bankier: https://!ithub.com/dbankier

• Jordi Domenec: https://!ithub.com/iamyellow

• Marcel Pociot: https://!ithub.com/mpociot

• Matt Apperson: https://!ithub.com/mattapperson

• Paul Mietz E!li: https://!ithub.com/pe!li

• Ruben Fonseca: https://!ithub.com/rubenfonseca

• Russ Frank: https://!ithub.com/russfrank

9

tiConf.eu, valencia, 24/02/2013

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 12

Runtime (iOS)

Titanium Modules

(API)

JS APP

Parser

Interpreter

IOS SDK

Bytecode!en

Java

Scrip

tCor

e

objective-cC++

KRO

LL B

RIDG

E

tiConf.eu, valencia, 24/02/2013 12

Runtime (iOS)

Titanium Modules

(API)

JS APP

Parser

Interpreter

IOS SDK

Bytecode!en

Java

Scrip

tCor

e

NO JIT

objective-cC++

KRO

LL B

RIDG

E

tiConf.eu, valencia, 24/02/2013

Titanium Modules

(API)

JS APP

Parser

Native Code

Android SDK

Native Code!en

KRO

LL B

RIDG

E

13

Runtime (Android)

JavaC++

V8

OPT

tiConf.eu, valencia, 24/02/2013

Titanium Modules

(API)

JS APP

Parser

Native Code

Android SDK

Native Code!en

KRO

LL B

RIDG

E

13

Runtime (Android)

JavaC++

V8

OPT

tiConf.eu, valencia, 24/02/2013

Titanium Modules

(API)

JS APP

Parser

Native Code

Android SDK

Native Code!en

KRO

LL B

RIDG

E

13

Runtime (Android)

JavaC++

V8

OPT

tiConf.eu, valencia, 24/02/2013

Terminology

14

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

Terminology

14

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();

moduleobject

tiConf.eu, valencia, 24/02/2013

Terminology

14

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();

moduleobject

Objects of t

he

Titanium API are

injected in th

e JS

environment at

app

startup

tiConf.eu, valencia, 24/02/2013

Terminology

14

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();

factorymethod

tiConf.eu, valencia, 24/02/2013

Terminology

14

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();

creation properties

tiConf.eu, valencia, 24/02/2013

Terminology

14

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();

proxyobject

tiConf.eu, valencia, 24/02/2013

Terminology

14

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();

view proxyobject

tiConf.eu, valencia, 24/02/2013

Terminology

14

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();

proxy property

tiConf.eu, valencia, 24/02/2013

Terminology

14

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();proxy

method

tiConf.eu, valencia, 24/02/2013

Proxies & Modules

Proxy

ViewProxy ViewModule

extends

has a

creates

15

mana!es

Native View TypeiOS UIView

Android View

extends

tiConf.eu, valencia, 24/02/2013

Proxies & Modules

Proxy

ViewProxy ViewModule

extends

has a

creates

15

mana!es

Native View TypeiOS UIView

Android View

State:properties

Actions: methods

Events:addEventListener(), fireEvent()

Interface

extends

tiConf.eu, valencia, 24/02/2013

Proxies & Modules

Proxy

ViewProxy ViewModule

extends

has a

creates

15

mana!es

Native View TypeiOS UIView

Android View

State:properties

Actions: methods

Events:addEventListener(), fireEvent()

Interface

Methods for the inte!ration within the application lifecycle•startup() (iOS)•shutdown() (iOS)•onAppCreate() (Android)

extends

tiConf.eu, valencia, 24/02/2013

Proxies & Modules

Proxy

ViewProxy ViewModule

extends

has a

creates

15

mana!es

Native View TypeiOS UIView

Android View

State:properties

Actions: methods

Events:addEventListener(), fireEvent()

Interface

Additional members for the inte!ration within the UI layout system:•add()•remove()•height•width•backgroundColor•...

Methods for the inte!ration within the application lifecycle•startup() (iOS)•shutdown() (iOS)•onAppCreate() (Android)

extends

tiConf.eu, valencia, 24/02/2013

Let’s create a module

tiConf.eu, valencia, 24/02/2013

• Create

• Develop

• Build

• Deploy

• Debu!

17

tiConf.eu, valencia, 24/02/2013

• Create

• Develop

• Build

• Deploy

• Debu!

17

tiConf.eu, valencia, 24/02/2013

Module Development

tiConf.eu, valencia, 24/02/2013

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

19

iOS Prerequisites

tiConf.eu, valencia, 24/02/2013

• 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)

★ !perf must be installed and in your system PATH.

★ [Eclipse]

20

Android Prerequisites

tiConf.eu, valencia, 24/02/2013 21

Create (cli)$ alias ti='~/Library/Application\ Support/Titanium/mobilesdk/osx/3.0.0.GA/ titanium.py'

OLD SCHOOL

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

OLD SCHOOL

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

OLD SCHOOL

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

tiConf.eu, valencia, 24/02/2013

Create (Ti Studio)

23

tiConf.eu, valencia, 24/02/2013

Create (Ti Studio)

24

tiConf.eu, valencia, 24/02/2013

Create (Ti Studio)

25

tiConf.eu, valencia, 24/02/2013

Module Boilerplate

26

tiConf.eu, valencia, 24/02/2013

Module Boilerplate

27

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/

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

Build (Ti Studio)

29

tiConf.eu, valencia, 24/02/2013

Build & Install (Ti Studio)

30

tiConf.eu, valencia, 24/02/2013

Deploy

31

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";

<?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

tiapp.xml

tiConf.eu, valencia, 24/02/2013

• Methods

• Passin! ar!uments

• Returnin! values

• Exceptions

• Usin! properties

• Proxies

• Events

• Callbacks

• ViewProxies

32

Agenda

tiConf.eu, valencia, 24/02/2013

Proxy/Module Methods

33

-(id)methodName:(id)args{ NSString result = @"Hello World”;

//do something

return result;}

return value

tiConf.eu, valencia, 24/02/2013

Proxy/Module Methods

33

-(id)methodName:(id)args{ NSString result = @"Hello World”;

//do something

return result;}

return value

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

no return value

tiConf.eu, valencia, 24/02/2013

Proxy/Module Methods

33

-(id)methodName:(id)args{ NSString result = @"Hello World”;

//do something

return result;}

return value

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

no return value

@Kroll.methodpublic String methodName(){

String result = "Hello world"; //do something

return result;}

return valueno ar!s

tiConf.eu, valencia, 24/02/2013

Proxy/Module Methods

33

-(id)methodName:(id)args{ NSString result = @"Hello World”;

//do something

return result;}

return value

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

no return value

@Kroll.methodpublic String methodName(){

String result = "Hello world"; //do something

return result;}

return valueno ar!s

@Kroll.methodpublic void methodName(String value){ //do something}

no return value

tiConf.eu, valencia, 24/02/2013

Example: xml2json module

34

Expected API

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

var json = xml2json.convert(xmlDoc);

https://!ithub.com/omorandi/TiXml2Json

tiConf.eu, valencia, 24/02/2013

Implementation

35

@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

Implementation

35

@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

NSArray of ar!uments

tiConf.eu, valencia, 24/02/2013

Implementation

35

@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

NSArray of ar!uments#define ENSURE_SINGLE_ARG(x,t)\{ \ x = (t*)[x objectAtIndex:0]; \} \

TiBase.h

tiConf.eu, valencia, 24/02/2013

Utility Macros

36

#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)

...

TiBase.h

tiConf.eu, valencia, 24/02/2013

Types

37

NSStringNSDictionaryNSArrayNSNumberNSDateNSNullTiProxy

Supported Directly

#import "TiUtils.h"

CGFloat f = [TiUtils floatValue:arg];NSInteger f = [TiUtils intValue:arg];

NSString *value = [TiUtils stringValue:arg];

TiColor *bgcolor = [TiUtils colorValue:arg];UIColor *backgroundColor = [bgcolor color];

Conversion Utilities

tiConf.eu, valencia, 24/02/2013

Return Values

• Sin!le Value (NSStrin!, NSNumber, …)

• Collections (NSArray)★ Converted into a JS Array object

• Dictionary (NSDictionary)★ Converted into a JS object★ key->value ===> property->value

• Proxy (TiProxy)

38

tiConf.eu, valencia, 24/02/2013

Return Values

• Sin!le Value (NSStrin!, NSNumber, …)

• Collections (NSArray)★ Converted into a JS Array object

• Dictionary (NSDictionary)★ Converted into a JS object★ key->value ===> property->value

• Proxy (TiProxy)

38

RETURN AUTORELEASED OBJECTS

tiConf.eu, valencia, 24/02/2013

xml2json Android

39

import org.appcelerator.kroll.KrollDict;

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

@Kroll.methodpublic KrollDict convert(String xml)

{ KrollDict json = null;

//do conversion stuff

return json; }

}

tiConf.eu, valencia, 24/02/2013

xml2json Android

39

import org.appcelerator.kroll.KrollDict;

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

@Kroll.methodpublic KrollDict convert(String xml)

{ KrollDict json = null;

//do conversion stuff

return json; }

}

public class KrollDict extends HashMap<String, Object>

KrollDict.java

tiConf.eu, valencia, 24/02/2013

Types

40

StringintfloatdoublebooleanObject[]HashMap<String, Object>TiProxy

import org.appcelerator.titanium.util.TiConvert;

int val = TiConvert.toInt(obj);float val = TiConvert.toFloat(obj);double val = TiConvert.toDouble(obj);boolean val = TiConvert.toBoolean(obj);int color = TiConvert.toColor(str);...

Supported Directly Conversion Utilities

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

tiConf.eu, valencia, 24/02/2013

Return Values

• Sin!le Value (Strin!, Inte!er, …)

• Collections (Object[])★ Converted into a JS Array object

• Dictionary (HashMap<Strin!, Object>)★ Converted into a JS object★ key->value ===> property->value

• Proxy (TiProxy)

41

tiConf.eu, valencia, 24/02/2013

Polymorphic Methods

42

-(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

Polymorphic Methods

43

public KrollDict convertFromString(String xml);public KrollDict convertFromBlob(TiBlob blob) @Kroll.methodpublic 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

Varargs

44

-(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

Varargs

45

@Kroll.methodpublic 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

Properties

46

module.propertyName = "HELLO";

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

set

!et

tiConf.eu, valencia, 24/02/2013

Properties

47

@interface TiMyModule: TiModule

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

@end

@implementation TiMyModule: TiModule

@synthesize propertyName;

@end

TiMyModule.h

TiMyModule.m

tiConf.eu, valencia, 24/02/2013

Properties: Setter/Getter

48

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

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

tiConf.eu, valencia, 24/02/2013

Properties: Setter/Getter

49

@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

Constants

50

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

Constants

51

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

https://!ithub.com/omorandi/TiSMSDialo!/blob/master/Classes/ComOmorandiSMSDialo!Proxy.m

tiConf.eu, valencia, 24/02/2013

Constants

51

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

https://!ithub.com/omorandi/TiSMSDialo!/blob/master/Classes/ComOmorandiSMSDialo!Proxy.m

#define MAKE_SYSTEM_PROP(name,map) \-(NSNumber*)name \{\return [NSNumber numberWithInt:map];\}\

TiBase.h

tiConf.eu, valencia, 24/02/2013

Constants

51

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

https://!ithub.com/omorandi/TiSMSDialo!/blob/master/Classes/ComOmorandiSMSDialo!Proxy.m

#define MAKE_SYSTEM_PROP(name,map) \-(NSNumber*)name \{\return [NSNumber numberWithInt:map];\}\

TiBase.h

@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

Proxy Objects

52

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

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

smsDialog.open();

tiConf.eu, valencia, 24/02/2013

Creating a Proxy

53

@interface TiIosSmsSMSDialogProxy: TiProxy<MFMessageComposeViewControllerDelegate>

@end

@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

TiIosSmsSMSDialo!Proxy.h

TiIosSmsSMSDialo!Proxy.m

tiConf.eu, valencia, 24/02/2013

Proxy Objects

54

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

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

sms.send();

tiConf.eu, valencia, 24/02/2013

Creating a Proxy

55

@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

Events

56

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

var scanner = tibarcode.createScanner();

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

Notify a chan!e of state, or an asynchronous event

tiConf.eu, valencia, 24/02/2013

Events

57

@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

Events

58

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

Callbacks

59

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

Notify the result of an asynchronous action

tiConf.eu, valencia, 24/02/2013

Callbacks

60

-(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

Callbacks

61

@Kroll.methodpublic 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

ViewProxy

62

View

Prox

y

View

Nativ

e Vi

ews

Hier

arch

y

Methods

Properties (!et/set)

Events

Holds the state of a view

Mana!es the native view hierarchy

tiConf.eu, valencia, 24/02/2013

ViewProxy

62

View

Prox

y

View

Nativ

e Vi

ews

Hier

arch

y

Methods

Properties (!et/set)

Events

Holds the state of a view

Mana!es the native view hierarchy

JS THREAD

tiConf.eu, valencia, 24/02/2013

ViewProxy

62

View

Prox

y

View

Nativ

e Vi

ews

Hier

arch

y

Methods

Properties (!et/set)

Events

Holds the state of a view

Mana!es the native view hierarchy

JS THREAD UI THREAD

tiConf.eu, valencia, 24/02/2013

ViewProxy

62

View

Prox

y

View

Nativ

e Vi

ews

Hier

arch

y

Methods

Properties (!et/set)

Events

Holds the state of a view

Mana!es the native view hierarchy

JS THREAD UI THREAD

Mostly async

tiConf.eu, valencia, 24/02/2013

Super-Smooth TableView

63

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

tiConf.eu, valencia, 24/02/2013

Implementation

64

@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

Messa!esViewProxy

@interface TiSmoothMessagesViewProxy : TiViewProxy

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

@end

TiSmoothMessa!esViewProxy.h

TiSmoothMessa!esViewProxy.m

tiConf.eu, valencia, 24/02/2013

Implementation

64

@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

Messa!esViewProxy

@interface TiSmoothMessagesViewProxy : TiViewProxy

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

@end

TiSmoothMessa!esViewProxy.h

TiSmoothMessa!esViewProxy.m

create the view and call addMessa!e on the UI thread

tiConf.eu, valencia, 24/02/2013

Implementation

65

@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

Messa!esView

@interface TiSmoothMessagesView : TiUIView {

MessagesViewController *viewController;}

@end

TiSmoothMessa!esView.h

TiSmoothMessa!esView.m

tiConf.eu, valencia, 24/02/2013

Implementation

65

@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

Messa!esView

@interface TiSmoothMessagesView : TiUIView {

MessagesViewController *viewController;}

@end

TiSmoothMessa!esView.h

TiSmoothMessa!esView.m

called by Titanium at view creation

tiConf.eu, valencia, 24/02/2013

Implementation

65

@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

Messa!esView

@interface TiSmoothMessagesView : TiUIView {

MessagesViewController *viewController;}

@end

TiSmoothMessa!esView.h

TiSmoothMessa!esView.m

called by Titanium at view creation

called by Titanium for notifyin! a chan!e in frame size

tiConf.eu, valencia, 24/02/2013 66

//// 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

module.xcconfi!

Module Packa!e(.zip)

build & packa!e

app bundle

tiConf.eu, valencia, 24/02/2013 66

//// 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

module.xcconfi!

Module Packa!e(.zip)

build & packa!e

app bundle

tiConf.eu, valencia, 24/02/2013 67

<?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>

timodule.xml

Module Packa!e(.zip)

build & packa!e AndroidManifest.xml

app.apk

tiConf.eu, valencia, 24/02/2013

Debu!!in!

tiConf.eu, valencia, 24/02/2013

Create a debug build

69

$ sed s/Release/Debug/ build.py > build_debug.py

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

build_debu!.py

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

build.py

tiConf.eu, valencia, 24/02/2013

Debugging

70

tiConf.eu, valencia, 24/02/2013

Debug Logs

71

@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

Android DDMS

72

tiConf.eu, valencia, 24/02/2013

Thank you!