OpenAphid-Engine

JavaScript&OpenGL ES Powered 2D Game Engine for Native iOS/Android Apps

Tutorial: Calling Objective-C Methods From JavaScript in OpenAphid-Engine

OpenAphid-Engine 0.2 was shipped with a new binding system which can bridge JavaScript functions to Objective-C methods on demand. It’s useful for games to integrate analytics services, monetization solutions, and social services, etc. In this tutorial, we’ll go through the binding system by demonstrating how to integrate Google Analytics iOS SDK into OpenAphid-Engine.

How to Access Objective-C Methods in JavaScript

The OABindingProtocol protocol, defined in OABindingProtocol.h, defines a method bindSelectors: that you can implement in your Objective-C classes to expose their methods to the JavaScript environment. To make a method valid for export, its return type and all argument types must be the supported types below:

Type Conversion of Return Value from Objective-C to JavaScript

Objective-C JavaScript
void undefined
nil or NSNull null
primitive numeric types
(int, float, double, etc)
number
NSNumber number
NSString string
NSArray array
NSDictionary object

Type Conversion of Argument Value from JavaScript to Objective-C

JavaScript Objective-C
undefined nil or NSNull
null nil or NSNull
number primitive number or NSNumber
string NSString
array NSArray
object except array NSDictionary

For any method to export, it must be explicitly declared in the implementation of bindSelectors: in your Objective-C class. For example, the snippet below exports [DatastoreBinding saveString:] to JavaScript environment as int saveString(string):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#import "OABindingProtocol"
@interface DatastoreBinding : NSObject <OABindingProtocol>
@end

@implementation DatastoreBinding
- (BOOL) saveString:(NSString*)content
{
  [_myDatastore save:content];
}

#pragma OABindingProtocol
- (void) bindSelectors:(OABindingMap*)bindingMap
{
  [bindingMap bindSelector:@selector(saveString:) forName:@"saveString"];
}
@end

The binding object need be injected into JavaScript via setScriptBinding:name:iOSOnly: method of OAGLViewController:

1
2
3
4
5
6
7
8
9
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  //...
  [self.viewController.glViewController setScriptBinding:[[[DatastoreBinding alloc] init] autorelease]
                                                    name:@"datastore"
                                                 iOSOnly:YES
   ];
  //...
}

OpenAphid-Engine injects an instance of DatastoreBinding as a JavaScript object of [object DynamicBinding] into JavaScript environment. And its name is datastore. The iOSOnly argument manages the namespace which contains the injected object. If it’s YES, then the injected object can be accessed via aphid.extios.datastore; otherwise it can be accessed via aphid.ext.datastore.

Notes: the iOSOnly argument is actually designed for future compliance when OpenAphid-Engine supports both iOS and Android.

Integration with Google Analytics in Boilerplate-iOS

Let’s see a more concrete example about integrating Google Analytics in Boilerplate-iOS.

After adding Google Analytics iOS SDK into the Xcode project by following its official instructions. We create an Objective-C object to describe the binding in OAGoogleAnalyticsBinding.h:

1
2
@interface OAGoogleAnalyticsBinding : NSObject <OABindingProtocol>
@end

Then we add implementations of several methods in OAGoogleAnalyticsBinding.m that we want to invoke in JavaScript:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@implementation OAGoogleAnalyticsBinding
//...

- (void) startTrackerWithAccountID:(NSString*)accountID despatchPeriod:(int)period
{
  [[GANTracker sharedTracker] startTrackerWithAccountID:accountID dispatchPeriod:period delegate:nil];
}

- (BOOL)trackPageview:(NSString *)pageURL
{
  return [[GANTracker sharedTracker] trackPageview:pageURL withError:NULL];
}

//...

The binding of the methods should be declared in the bindSelectors: method:

1
2
3
4
5
6
7
8
9
10
11
12
- (void)bindSelectors:(OABindingMap *)bindingMap
{
[bindingMap bindSelector:@selector(startTrackerWithAccountID:despatchPeriod:)
                 forName:@"startTracker"];

//...

[bindingMap bindSelector:@selector(trackPageview:)
                 forName:@"trackPageView"];

//...
}

Then we can inject it into JavaScript as following code inside OAAppDelegate.m:

1
2
3
4
5
6
7
8
9
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  //...

  [self.viewController.glViewController setScriptBinding:[[OAGoogleAnalyticsBinding new] autorelease]
                                                    name:@"gat"
                                                 iOSOnly:NO];
  //...
}

Updates at 07-09-2012: iOSOnly has been changed from YES to NO to match its Android version.

Now we can use Google Analytics in JavaScript to track the user’s behavior in games:

1
2
3
var gat = aphid.ext.gat; //Google Analytics is injected as aphid.ext.gat
gat.startTracker("UA-31741840-1", 10); //start a tracker
gat.trackPageView("main.js"); //track a page view

Updates at 07-09-2012: Since iOSOnly has been set to NO, the JavaScript namespace for gat is switched to aphid.ext from aphid.extios

We’re going to integrate more services into Boilerplate-iOS, and make it be a better starter kit for game development with OpenAphid-Engine.

If you have better ideas, please feel free to contact us via openaphid@gmail.com or raise an issue in our github repositories.

Comments