Extending Titanium with native iOS and Android modules

Preview:

DESCRIPTION

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

Citation preview

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!

Recommended