extending titanium with native ios and android modules
DESCRIPTION
This is the presentation used for the workshop on Titanium module development held at tiConf 2013 in ValenciaTRANSCRIPT
tiConf.eu, valencia, 24/02/2013
Native iOS & Android Modules
Extending Titanium
tiConf.eu, valencia, 24/02/2013
Olivier MorandiSoftware en!ineer
http://[email protected]@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
• http://docs.appcelerator.com/titanium/latest/#!/!uide/Extendin!_Titanium_Mobile
• http://docs.appcelerator.com/titanium/latest/#!/!uide/iOS_Module_Development_Guide
• http://docs.appcelerator.com/titanium/latest/#!/!uide/Android_Module_Development_Guide
6
Official Appcelerator Guides
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!