-
Notifications
You must be signed in to change notification settings - Fork 13
Internals
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.
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
Even though the notation uses Objective-C, you will find equivalent Java-Methods as well.
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)] )
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.
- For instances of
TransitNativeFunction
you have to calldispose
manually (see blow). - dispose calls
[context releaseJSProxyWithId: proxyId]
- proxies will be disposed when the JS will be discared (e.g. a page reload)
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
.
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]`
- 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!
Typically received as arguments when you implement a TransitNativeFunction
or manually created like
jsFunc = [context eval:@"function(a,b){return a+b}"];
-
-(void)callAsyncWithThisArg:(id)thisArg arguments:(NSArray*)arguments
(can optionally) puts itself to a queue via[context queueAsyncCallToJSFunction:self thisArg:thisArg arguments:arguments];
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'
.
- it's
values
notarguments
!
-
context
property isnil
-
doInvokeNative
(and optionallydoHandleInvocationQueue
) somehow implements the expectedtransit.doInvokeNative
and might differ in the actual signature. In * any case, it (or both methods) eventually callinvokeNativeWithDescription:
- on disposal, it should free all bound resources with
disposeAllNativeProxies
anddrainJSProxies
![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] )
On Javascript Side, there's a single transit
object with a few functions.
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 = 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:
-
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" -
typeof element == "object"
andelement.constructor != Object
and not Array, String, Number, Boolean → retain element with new id, push magic obj id -
JSON.stringify(element)
fails → retain element with new id, push "magic obj id" -
value = JSON.stringify(element)
succeeds- parse again with
obj = JSON.parse(value)
(strips off any function, values becomenull
) - recursively iterate over
obj
andelement
. For each function that has been stripped:- retain function
- → store "magic function id" at missing property
-
push
obj
- parse again with
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 = 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 = 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 = 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 = 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.
Several functions rely on so-called "magic ids". Here's how they look like:
__TRANSIT_JS_FUNCTION_<ID>
__TRANSIT_OBJECT_PROXY_<ID>
__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.