Skip to content
HBehrens edited this page Feb 18, 2013 · 13 revisions

Transit consists of a set of classes on the native side (native runtime) as well as a global transit object inside the JavaScript runtime. Here, you will find some internal information about both couterparts

If you want to follow the source while reading this page, please open the

in a separate window.

Limitations

The chosen design has a few known limitations.

  • no modification on passed arguments
  • life-time management for native functions has to be done manually
  • iPhone cannot handle more than 63 reentrant back to webview

Native Runtime

Even though the notation uses Objective-C, you will find equivalent Java-Methods as well.

TransitObject

Values for thisArg, arguments (functions) and values (eval) can be any compatible (see link) plain Objects. TransitObject is never created by the client but created by Transit for

  • functions (native or from JS context),
  • cyclic objects that cannot otherwise be passed from JS context

There's typically only on instance of TransitContext that keeps strong references to TransitNativeFunction (see below). TransitCallScope can be used when a client provides implementations for TransitNativeFunction (see blow).

![Class Diagram: Transit Object](http://yuml.me/diagram/dir:LR;scale:80;/class/ [TransitObject|context|objectForKey;callMember:arguments:;(+ convenience)], [TransitObject]^-[TransitProxy|disposed;dispose], [TransitProxy]^-[TransitFunction|callWithThisArg:arguments:;(+ convenience)], [TransitFunction]^-[TransitJSFunction], [TransitFunction]^-[TransitNativeFunction|async;noThis], [TransitObject]^-[TransitEvaluable|eval:thisVar:values;(+ convenience)], [TransitEvaluable]^-[TransitContext|currentCallScope|functionWith..;(+ convenience)], [TransitEvaluable]^-[TransitCallScope|(see below)] )

TransitProxy

This object represents a cyclic object or JavaScript function that cannot directly be transfered from the JS contet into the native runtime. Instead, it holdes a proxyId that can be used to dereference or call it at a later in the JS context again. The JS runtime will keep a strong reference to these objects until dispose has been called. This happens transparently as soon as the instance is being deallocated/garbage collected by the native runtime.

Notes

  • For instances ofTransitNativeFunction you have to call dispose manually (see blow).
  • dispose calls [context releaseJSProxyWithId: proxyId]
  • proxies will be disposed when the JS will be discared (e.g. a page reload)

TransitFunction

General super type to invoke a method. thisArg and arguments can be of any compatible type including TransitFunction to call another function with a function.

- (id)callWithThisArg:(id)thisArg arguments:(NSArray *)arguments returnResult:(BOOL)returnResult;
- (void)callAsyncWithThisArg:(id)thisArg arguments:(NSArray *)arguments;

Use returnResult=NO to increase the performance of a potential call to the JS runtime as well as callAsync… if you can live with a non-blocking call, too.

In addition to these generic methods you see above, there are several convenience methods like a simple call.

TransitNativeFunction

Represents a piece of native code that can be accessed by JS code. Once created a native function func you would typically pass it to the JS context:

[context eval:@"button.onclick=@" value:func]`

Notes

  • proxyId == nativeId
  • dispose calls [context releaseNativeFunctionWithId:self.nativeId] but not [context releaseJSProxyWithId: self.proxyId]!
  • ids are unique over time and for each JavaScript context. That includes a page reload!

TransitJSFunction

Typically received as arguments when you implement a TransitNativeFunction or manually created like

jsFunc = [context eval:@"function(a,b){return a+b}"];

Notes

  • -(void)callAsyncWithThisArg:(id)thisArg arguments:(NSArray*)arguments (can optionally) puts itself to a queue via [context queueAsyncCallToJSFunction:self thisArg:thisArg arguments:arguments];

TransitEvaluable

Allows you to evaluate a piece of JavaScript. The jsCode can contain @ placemarkers that will be replaced with the provided values from left to right. For expressions like this.prop you can pass an explicit thisArg or omit it bind this to the global object.

-(id)eval:(NSString*)jsCode thisArg:(id)thisArg values:(NSArray*)values returnJSResult:(BOOL)returnResult;

There are several convenience methods like a simple eval:@"alert(42)" to make your life easier!

The different variations of eval take the @-sign in the JavaScript to fill-in arguments. If you want to have the character '@' in JavaScript, use the unicode char '\u0040'.

Notes

  • it's values not arguments!

TransitContext

Notes

  • context property is nil
  • doInvokeNative (and optionally doHandleInvocationQueue) somehow implements the expected transit.doInvokeNative and might differ in the actual signature. In * any case, it (or both methods) eventually call invokeNativeWithDescription:
  • on disposal, it should free all bound resources with disposeAllNativeProxies and drainJSProxies

TransitCallsScope

![Class Diagram: Transit Object](http://yuml.me/diagram/dir:LR;scale:80;/class/ [TransitCallScope|parentScope;thisArg;expectsResult], [TransitCallScope]^-[TransitFunctionCallScope|function;arguments|forwardToFunction:], [TransitFunctionCallScope]^-[TransitJSFunctionCallScope], [TransitFunctionCallScope]^-[TransitNativeFunctionCallScope], [TransitCallScope]^-[TransitEvalCallScope|jsCode;values], [TransitCallScope]^-[TransitAsyncCallScope] )

JavaScript Runtime

On Javascript Side, there's a single transit object with a few functions.

transit.nativeFunction

Used to create native call-outs. Any native call like

[transit eval:@"alert(%@)" values:nativeFunction];

will be translated to

alert(transit.nativeFunction('someInternalId'));

Internally, it's a factory to create a wrapper for invokeNative or queueNative.

transit.invokeNative

transit.invokeNative = function(nativeId, thisArg, args, noThis)

This is never called directly, but indirectcly from nativeFunction(nativeId) instead. It first builds a invocationDescription = {nativeId: nativeId, arguments:[]} object from each element of otherArgs by analyzing its nature and push a suitable value as argument to the invocationDescription. Each element falls into one of the following cases:

  1. typeof element == "function" a. it's a native wrapper → then push "magic native function id"
    b. otherwise → retain func with new Id, push "magic function id"
  2. typeof element == "object" and element.constructor != Object and not Array, String, Number, Boolean → retain element with new id, push magic obj id
  3. JSON.stringify(element) fails → retain element with new id, push "magic obj id"
  4. value = JSON.stringify(element) succeeds
    1. parse again with obj = JSON.parse(value) (strips off any function, values become null)
    2. recursively iterate over obj and element. For each function that has been stripped:
      1. retain function
      2. → store "magic function id" at missing property
    3. push obj

Similar to these steps, it stores thisArg as invocationDescription.thisArg before finishing with return transit.doInvokeNative(invocationDescription). If thisArg is the globalObject or noThis == true it will pass null instead.

transit.queueNative

transit.queueNative = function(nativeId, thisArg, args)

Similar, to transit.invokeNative but puts the invocationDescription onto a queue that will be handled if its length exceeds transit.invocationQueueMaxLen or on the next iteration of the run loop.

transit.doInvokeNative

transit.doInvokeNative = function(invocationDescription)

This function has to be overriden by the native runtime to provide a blocked call-out. It must return the result as proper JavaScript value.

transit.doHandleInvocationQueue

transit.doHandleInvocationQueue = function(invocationDescriptions)

Similar to transit.doInvokeNative but takes an array of invocationDescription. The native runtime may or may not override this function. The default implementation iterates of the array and calls transit.doInvokeNative.

transit.releaseElementWithId

transit.releaseElementWithId = function(retainId)

Releases a previously retained reference to a function or an object. Will be called from the native runtime when this proxy is not needed anymore.

transit magic values

Several functions rely on so-called "magic ids". Here's how they look like:

  1. __TRANSIT_JS_FUNCTION_<ID>
  2. __TRANSIT_OBJECT_PROXY_<ID>
  3. __TRANSIT_NATIVE_FUNCTION_<ID>

Values for 1. and 2. are created on the JavaScript side. Values for 3. are an implementation detail from the native runtime.