# JSBridge
Applicable to version 7.0.4 and above
# What is JSBridge
?
JSBridge
is a mechanism implementated the ability to callclasses
,methods
,variables
ofObjective-C
fromJavaScript
.
# How to begin with JSBridge
?
- Learn
Objective-C
andiOS
development for an hour before coding withJSBridge
.- Whenever you want to make something, Google the
Objective-C
implementation first.- Use
use_jsbridge()
method to refer aclass
ofObjective-C
iniOS
.- Use
.thisIsAMethod()
to call a method ofObjective-C
, e.g.[alertView show]
=>alertView.show()
.- Use underline
_
to join method ofObjective-c
with multiple arguments, e.g.[UIView animateWithDuration:duration animations:animations]
=>UIView.animateWithDuration_animations(duration, animations)
- Use
.setProperty(value)
instead of.property = value
to set value to a property.- All stuff relative to
UI
much be put in the blockdispatch_async_main(function () {})
or it will crash.
# Usage and Examples
# How to make an UIAlertView
?
Original implementation with Objective-C
UIAlertView *alertView = [[UIAlertView alloc] init];
alertView.title = @"What is this?"
alertView.message = @"This is UIAlertView of iOS which is called through JSBridge, you can use JSBridge to call any class/methods/variables of iOS, it makes anything possible!";
[alertView addButtonWithTitle:@"OK"];
[alertView show];
Implement it with JSBridge
// Import required classes of iOS/Objective-C
const UIAlertView = use_jsbridge('UIAlertView')
// You can also use it directly like this: `use_jsbridge('UIAlertView').alloc().init()`
//-------------------------------------------------------------------------------
// !!!!!!!!ANYTHING relative to UI should be in dispatch_async_main block!!!!!!!!
//-------------------------------------------------------------------------------
dispatch_async_main(function () {
// Show an alert view of iOS
var alertView = use_jsbridge('UIAlertView').alloc().init();
alertView.setTitle('What is this?');
alertView.setMessage('This is UIAlertView of iOS which is called through JSBridge, you can use JSBridge to call any class/methods/variables of iOS, it makes anything possible!');
alertView.addButtonWithTitle('OK');
alertView.show();
})
# How to make an UIWindow
?
Original implementation with Objective-C
CGRect frame = CGRectMake(0, 0, 300, 300);
UIWindow *window = [[UIWindow alloc] initWithFrame:frame];
window.backgroundColor = [UICOlor redColor]; // this line equals to `[window setBackgroundColor:[UICOlor redColor]];` In Objective-c
[window setWindowLevel:1000];
[window makeKeyAndVisible];
Implement it with JSBridge
//-------------------------------------------------------------------------------
// !!!!!!!!ANYTHING relative to UI should be in dispatch_async_main block!!!!!!!!
//-------------------------------------------------------------------------------
dispatch_async_main(function () {
const frame = { x: 0, y: 0, width: 300, height: 300 }
const window = use_jsbridge('UIWindow').alloc().initWithFrame(frame)
const color = use_jsbridge('UIColor').redColor()
window.setBackgroundColor(color)
window.setWindowLevel(1000)
window.makeKeyAndVisible()
})
# How to open an app or an URL?
Original implementation with Objective-C
NSString *urlString = @"https://autotouch.net";
// open Preferences: `NSString *urlString = @"prefs:root=General&path=About"`
// open Music App: `NSString *urlString = @"musics://"`
// open an item in App Store: `NSString *urlString = @"itms-apps://itunes.apple.com"`
// open telephone: `NSString *urlString = @"tel://+1123456"`
// open Preferences: `NSString *urlString = @"prefs:root=General&path=About"`
// open Preferences: `NSString *urlString = @"prefs:root=General&path=About"`
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString]];
Implement it with JSBridge
// Open Map app with `JSBridge`
const url = use_jsbridge('NSURL').URLWithString('maps://');
use_jsbridge('UIApplication').sharedApplication().openURL(url);
# How to show an image?
Original implementation with Objective-C
CGRect frame = CGRectMake(0, 0, 300, 300);
UIWindow *window = [[UIWindow alloc] initWithFrame:frame];
window.backgroundColor = [UICOlor redColor]; // this line equals to `[window setBackgroundColor:[UICOlor redColor]];` In Objective-c
[window setWindowLevel:1000];
[window makeKeyAndVisible];
Implement it with JSBridge
const UIWindow = use_jsbridge('UIWindow')
const UIImageView = use_jsbridge('UIImageView')
const UIImage = use_jsbridge('UIImage')
//-------------------------------------------------------------------------------
// !!!!!!!!ANYTHING relative to UI should be in dispatch_async_main block!!!!!!!!
//-------------------------------------------------------------------------------
dispatch_async_main(function () {
// You need an UIWindow first
const frame = { x: 0, y: 0, width: 300, height: 300 }
const window = use_jsbridge('UIWindow').alloc().initWithFrame(frame)
const color = UIColor.redColor()
window.setBackgroundColor(color)
window.setWindowLevel(1000)
// Construct an UIImageView
const imageViewFrame = { x: 30, y: 50, width: 200, height: 200 }
const imageView = UIImageView.alloc().initWithFrame(imageViewFrame)
const image = UIImage.imageNamed('/var/mobile/Library/AutoTouch/Scripts/Examples/images/cat.png')
imageView.setImage(image)
// Put the UIImageView on the window
window.addSubview(imageView)
// Show the window
window.makeKeyAndVisible()
// use window.setHidden(true) to dismiss the window
})
# How to define a class for Objective-C?
Use
defineClass(classDeclaration, [properties,] instanceMethods, classMethods)
@param classDeclaration
: class name, super classname and protocols
@param properties
: new propertie names, a string array
@param instanceMethods
: instance methods you want to override or add
@param classMethods
: class methods you want to override or add
// Objective-C
@implementation MyTestObject
+ (void)shareInstance
{
}
@end
// JavaScript
defineClass("MyTableViewController", [
// new properties
], {
// instance methods
}, {
// Class static methods
shareInstance: function() {
...
},
})
# Override existing instance methods
Use single underline
_
to join multiple param names of the original method:
// Objective-C
@implementation MyTableViewController
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
}
@end
// JavaScript
defineClass("MyTableViewController", [], {
tableView_didSelectRowAtIndexPath: function(tableView, indexPath) {
...
},
})
Use double underlines
__
to replace single underline_
in the original method name of Objective-C:
// Objective-C
@implementation MyTableViewController
- (NSArray *) _dataSource {
}
@end
// JavaScript
defineClass("MyTableViewController", [], {
__dataSource: function() {
},
})
Call original method by adding prefix
ORIG
to the method name.
// Objective-C
@implementation MyTableViewController
- (void) viewDidLoad {
}
@end
// JavaScript
defineClass("MyTableViewController", [], {
viewDidLoad: function() {
self.ORIGviewDidLoad();
},
})
# Override existing class static methods
Same rules as instance methods
// Objective-C
@implementation MyTestObject
+ (void)shareInstance
{
}
@end
// JavaScript
defineClass("MyTableViewController", [
// new properties
], {
// instance methods
}, {
// Class static methods
shareInstance: function() {
...
},
})
# Override Category
methods
Same rules as overriding instand methods and class methods
// OBJC
@implementation UIView (custom)
- (void)methodA {
}
+ (void)clsMethodB {
}
@end
// JavaScript
defineClass('UIView', {
methodA: function() {
}
}, {
clsMethodB: function() {
}
});
# Use super()
self.super()
issuper
method ofObjective-C
, use it to call parent class's methods/properties
// JavaScript
defineClass("MyTableViewController", {
viewDidLoad: function() {
self.super().viewDidLoad();
}
})
# get/set properties
Use
get
andset
to get / set existing property
// Objective-C
@interface MyTableViewController
@property (nonatomic) NSArray *data;
@property (nonatomic) NSString *shareURL;
@property (nonatomic) NSString *shareTitle;
@end
@implementation MyTableViewController
@end
// JavaScript
defineClass("MyTableViewController", {
viewDidLoad: function() {
var data = self.data(); //get property value
self.setData(data.toJS().push("Something")); //set property value
var sel = self;
self.bridge().registerHandler_handler('h5ToNativeShareDialog', block('NSDictionary *',function(data,responseCallback) {
sel.setShareURL(data.objectForKey('url'));
sel.setShareTitle(data.objectForKey('title'));
}));
})
Use
getProp()
andsetProp_forKey()
to get / set new property
// Objective-C
@interface MyTableViewController
@end
@implementation MyTableViewController
@end
// JavaScript
defineClass("MyTableViewController", {
init: function() {
self = self.super().init()
self.setProp_forKey("Something", "data") //add new property (id data)
return self;
}
viewDidLoad: function() {
var data = self.getProp("data") //get the new property value
},
})
Put new property names at the second parameter of
defineClass()
// JavaScript
defineClass("MyTableViewController", ['data', 'totalCount'], {
init: function() {
self = self.super().init()
self.setData(["a", "b"]) // new property (id data)
self.setTotalCount(2)
return self
},
viewDidLoad: function() {
var data = self.data() // get property
var totalCount = self.totalCount()
},
})
Use
valueForKey()
andsetValue_forKey()
to get / set private variables
// Objective-C
@implementation MyTableViewController {
NSArray *_data;
}
@end
// JavaScript
defineClass("MyTableViewController", {
viewDidLoad: function() {
var data = self.valueForKey("_data") //get member variables
self.setValue_forKey(["Something"], "_data") //set member variables
},
})
# Add new methods
You can add new methods to the existing Class, if you want to call the new method in Objective-C, all the params type is
id
type of Objective-c.
// Objective-C
@implementation MyTableViewController
- (void)viewDidLoad
{
NSString* data = [self dataAtIndex:@(1)];
NSLog(@"%@", data); //output: Patch
}
@end
// JavaScript
var data = ["JS", "Patch"]
defineClass("MyTableViewController", {
dataAtIndex: function(idx) {
return idx < data.length ? data[idx]: ""
}
})
You can make class conforms to protocols:
// JavaScript
defineClass("MyViewController: UIViewController<UIScrollViewDelegate, UITextViewDelegate>", {
})
If you want to add method that defined in protocol and params or return types is not
id
, you must conforms to the protocol.
// Objective-C
@protocol UIAlertViewDelegate <NSObject>
...
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex;
...
@end
// JavaScript
defineClass("MyViewController: UIViewController <UIAlertViewDelegate>", {
viewDidAppear: function(animated) {
var alertView = use_jsbridge('UIAlertView')
.alloc()
.initWithTitle_message_delegate_cancelButtonTitle_otherButtonTitles(
"Alert",
self.dataSource().objectAtIndex(indexPath.row()),
self,
"OK",
null
)
alertView.show()
}
alertView_clickedButtonAtIndex: function(alertView, buttonIndex) {
console.log('clicked index ' + buttonIndex)
}
})
# How to use struct
of Objctive-C?
AutoTouch has preset for structs of Obejctive-C:
CGRect
,CGPoint
,CGSize
,NSRange
, you can use them directly.
// Objective-C
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(20, 20, 100, 100)];
[view setCenter:CGPointMake(10,10)];
[view sizeThatFits:CGSizeMake(100, 100)];
CGFloat x = view.frame.origin.x;
NSRange range = NSMakeRange(0, 1);
// JavaScript
const view = UIView.alloc().initWithFrame({x:20, y:20, width:100, height:100})
view.setCenter({x: 10, y: 10})
view.sizeThatFits({width: 100, height:100})
const x = view.frame().x
const range = {location: 0, length: 1}
You can use
defineStruct
to define other structs beside the four
// JavaScript
defineStruct({
name: 'StructName',
types: 'SFF', // look below
keys: ['propertyName1', 'propertyName1', 'propertyName3']
})
// type values:
'c': char
'C': unsigned char
's': short
'S': unsigned short
'i': int
'I': unsigned int
'l': long
'L': unsigned long
'q': long long
'Q': unsigned long long
'f': float
'F': CGFloat
'N': NSInteger
'U': NSUInteger
'd': double
'B': BOOL
Examples
// Objective-C
struct DemoStruct {
CGFloat a;
long b;
double c;
BOOL d;
}
@implementation DemoClass
+ (void)passStruct:(DemoStruct)s;
+ (DemoStruct)returnStruct;
@end
// JavaScript
defineStruct({
name: 'DemoStruct',
types: 'FldB',
keys: ['a', 'b', 'c', 'd']
})
const DemoClass = use_jsbridge('DemoClass')
DemoClass.passStruct({a:1, b:2, c:4.2, d:1})
const s = DemoClass.returnStruct()
# How to use selector
of Objective-C?
// Objective-C
[self performSelector:@selector(viewWillAppear:) withObject:@(YES)];
// JavaScript
self.performSelector_withObject("viewWillAppear:", 1)
# How to use nil
of Objective-C?
null
,undefined
ofJavaScript
equalnil
,NULL
ofObjective-C
nsnull
ofJavaScript
equalsNSNull
ofObjective-C
// Objective-C
@implementation TestObject
+ (BOOL)testNull(NSNull *null) {
return [null isKindOfClass:[NSNull class]]
}
@end
// JavaScript
use_jsbridge('TestObject').testNull(nsnull) //return 1
use_jsbridge('TestObject').testNull(null) //return 0
var url = "";
var rawData = NSData.dataWithContentsOfURL(NSURL.URLWithString(url));
if (rawData != null) {} //Don't use this to check null
if (!rawData){} // Use this to check null
# Use NSArray
, NSString
, NSDictionary
of Objective-C
You should use
NSArray
,NSString
,NSDictionary
of Objective-C as a commonNSObject
// Objective-C
@implementation DemoObject
+ (NSArray *)data
{
return @[[NSMutableString stringWithString:@"JS"]]
}
+ (NSMutableDictionary *)dict
{
return [[NSMutableDictionary alloc] init];
}
@end
// JavaScript
const DemoObject = use_jsbridge('DemoObject')
const ocStr = DemoObject.data().objectAtIndex(0)
ocStr.appendString("Something")
const dict = DemoObject.dict()
dict.setObject_forKey(ocStr, 'name')
console.log(dict.objectForKey('name'))
You can also use
.toJS()
to covertNSArray
,NSString
,NSDictionary
to array, string, dict ofJavaScript
// JavaScript
var data = use_jsbridge('DemoObject').data().toJS()
// data instanceof Array === true
data.push("Ok")
const dict = DemoObject.dict()
dict.setObject_forKey(data.join(''), 'name')
dict = dict.toJS()
console.log(dict['name'])
# How to use block
of Objective-C?
// Objective-C
@implementation DemoObject
+ (void)request:(void(^)(NSString *content, BOOL success))callback
{
callback(@"I'm content", YES);
}
@end
// JavaScript
use_jsbridge('DemoObject').request(block("NSString *, BOOL", function(ctn, succ) {
if (succ) log(ctn)
}))
// Objective-C
@implementation DemoObject
typedef void (^JSBlock)(NSDictionary *dict);
+ (JSBlock)genBlock
{
NSString *ctn = @"Submit";
JSBlock block = ^(NSDictionary *dict) {
NSLog(@"I'm %@, version: %@", ctn, dict[@"v"])
};
return block;
}
+ (void)execBlock:(JSBlock)blk
{
}
@end
// JavaScript
const blk = use_jsbridge('DemoObject').genBlock();
blk({v: "0.0.1"});
//-------------------
var blk = use_jsbridge('DemoObject').genBlock();
blk({v: "0.0.1"});
use_jsbridge('DemoObject').execBlock(block("id", blk));
// Objective-C
+ (void)requestUrl:(NSString *)url withCallback:(void(^)(id data))callback;
// JavaScript
defineClass('TestObject', {
requestUrl_withCallback: function(url, callback) {
self.handleCallback(block('id', callback));
}
});
How to use
self
in ablock
?
// JavaScript
defineClass("MyViewController", {
viewDidLoad: function() {
var slf = self;
use_jsbridge("TestObject").callBlock(block(function(){
//`self` is not available here, use `slf` instead.
slf.doSomething();
});
}
}
# __weak
, __strong
// Objective-C
- (void)test {
__weak id weakSelf = self;
[self setCompleteBlock:^(){
[weakSelf blabla];
}]
}
// JavaScript
var weakSelf = __weak(self)
self.setCompleteBlock(block(function(){
weakSelf.blabla();
}))
// How to convert it to `__strong`
var weakSelf = __weak(self)
self.setCompleteBlock(block(function(){
var strongSelf = __strong(weakSelf)
strongSelf.blabla();
}))
# GCD
// Objective-C
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// do something
});
dispatch_async(dispatch_get_main_queue(), ^{
// do something
});
// JavaScript
dispatch_after(1.0, function(){
// do something
})
dispatch_async_main(function(){
// do something
})
dispatch_sync_main(function(){
// do something
})
dispatch_async_global_queue(function(){
// do something
})
# Constants, enum, macro, global variables
You can't use Contants and enums of Objective-C directly in JavaScript
// Objective-C
[btn addTarget:self action:@selector(handleBtn) forControlEvents:UIControlEventTouchUpInside];
// JavaScript
// value of UIControlEventTouchUpInside is 1<<6 from Apple's document
btn.addTarget_action_forControlEvents(self, "handleBtn", 1<<6);
// OR redefine the constants in js
//js
const UIControlEventTouchUpInside = 1 << 6;
btn.addTarget_action_forControlEvents(self, "handleBtn", UIControlEventTouchUpInside);
Macro of Objective-C
// Objective-C
#define TABBAR_HEIGHT 40
#define SCREEN_WIDTH [[UIScreen mainScreen] bounds].size.height
[view setWidth:SCREEN_WIDTH height:TABBAR_HEIGHT];
// JavaScript
view.setWidth_height(UIScreen.mainScreen().bounds().height, 40);
Get global variable defined in an Objective-C class
// Objective-C
static NSString *name;
@implementation TestObject
+ (NSString *)name {
return name;
}
@end
// JavaScript
const name = use_jsbridge('TestObject').name()