When iOS loves JS

Think of a video City chubby JS mixed development previously seen in the Mu class network @, this sort of knowledge point between iOS and JS.

JSBinding Technology


JSBinding technology is not a Hybrid technology; it is a bridge between JavaScript and Native.

When iOS loves JS

JSBinding relies on JSEngine, and iOS 7 first opened JavaScriptCore’s API, enabling JSBinding to break through the gap between JS and native language.

JavaScriptCore core classes:

  • JavaScriptCore.h
  • JSContext
  • JSValue
  • JSExport

First you need to import the JavaScriptCore header file

#import < JavaScriptCore/JavaScriptCore.h>

Then create the running context

JSContext *context = [[JSContext alloc] init]; / / JSContext is the JS runtime environment

Add JS exception handler

Context.exceptionHandler ^ (JSContext *ctx, JSValue = *exception) {NSLog ("% @ @", exception);};

1. Native in tune JS

  • The execution of JS Code: JSValue *value = [context evaluateScript:@ “1+2”]; NSLog (@ “1 + 2 =%f, [result toDouble]); / / 1 + 2 = 3
  • JS function: NSString *script = “@ var function sum (a, b) {return} a+b; [context; evaluateScript:script]; JSValue” *sum = context[@ “sum”]; JSValue *result = [sum callWithArguments:@[@1, @2]]; NSLog (@ sum =%f (1,2), [result toDouble] (1,2); / / sum = 3)
  • Create JS values: JSValue *intVar = [JSValue valueWithInt32:123 inContext:context]; context[@ “bar”] = intVar; / / does not create a global variable to the value object [context evaluteScript:@ “bar++”]; another way, directly in the context creates a global variable “var [context evaluateScript:@ bar] = 123;”;

2. JS tune through Native Block

Context[@ "sum"] = ^ (int a, int b) {return}; a + B; JSValue *result = [context evaluateScript:@ sum (1,2) "]; NSLog (@ sum (1,2) =%f, [result toDouble]); / / sum (1,2) = 3

You can pass more than one parameter

Context[@ "sum"] = ^{NSArray *args = [JSContext currentArguments]; __block double sum = 0; [args enumerateObjectsUsingBlock:^ (ID _Nonnull obj, NSUInteger IDX, BOOL * _Nonnull stop) {sum [obj = toDouble];}]; return sum;}; JSValue *result = [context evaluateScript:@ sum (1,2,3,4,5)] NSLog (@ ";" sum =%f, [result toDouble]); / / sum = 15

3. tune through Native JSExport

Instantiate the JSExport class, add it to JSContext, and call the object with JS

Create a new JSExport class Point3D

#import < JavaScriptCore/JavaScriptCore.h> / / @protocol Point3DExport < to declare an agreement; JSExport> / / @property double attribute is exposed to JS X; @property double y; @property double Z; / / the method exposed to JS - (double) length @interface Point3D: NSObject @ end; < Point3DExport> JSContext {*context} - (instancetype; initWithContext: (JSContext) * CTX @end @implementation Point3D @synthesize); X, y, Z; (instancetype) - initWithContext: (JSContext * CTX) {if (self = [super init]) {context = CTX; / / try to Point3D in context, but actually only added a object, instead of a class context[@ "Point3D"] = [Point3D class] return self;};} - {(double) length return sqrt (self.x * self.x + self.y * self.y + self.z * self.z)} @end

Executive part

Point3D *point3D [[Point3D alloc] = initWithContext:context]; point3D.x = 1; point3D.y = 2; point3D.z = 3; context[@ "point3D"] = point3D; NSString = *script @ point3D.x=0; point3D.y=4; point3D.length (JSValue); *result = [context evaluateScript: script]; NSLog (@ "Result of is script% @%f, [result, toDouble]); / / Result of point3D.x=0; point3D.y=4; point3D.length (is) 5

4. load the JS file directly

Void loadScriptContext (JSContext *ctx, NSString *fileName) {NSString *filePath = [NSString stringWithFormat:@ "%@/JS/%@" [[NSBundle, mainBundle] resourcePath], fileName]; NSString *script = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]; [ctx evaluateScript:script];}

You can borrow 3 to simulate the console’s function, and then use [JSContext currentArguments] to get all the parameters printed.

About type conversions between JS and OC

Type conversion of When iOS loves JS,
, OC, and JS

About Retain cycle processing between JS and OC

Using JSManagedValue to wrap JS objects, the system handles garbage collection and memory management correctly.

[JSManagedValue managedValueWithValue:value];

About multithreading between JS and OC

The same JSVirtualMachine is equivalent to being in the same thread, and there is no need to worry about locking and concurrency problems.

/ / initialize the virtual machine, similar to creating a new thread JSVirtualMachine *jsvm = [[JSVirtualMachine alloc] init]; JSContext *ctx = [[JSContext alloc] initWithVirtualMachine:jsvm];

Hybrid


Mixed development, provide a unified API by Native by JSBridge method, and then to write the actual use of Html5+JS logic, called API, under this model, due to Android, iOS API are generally consistent, and the final page is displayed in the webView, so there is a cross platform effect. The more famous frames are PhoneGap (Cordova), AppCan, etc..
Hybrid is the bridge between Web technology and Native!

When iOS loves JS

1. native code calls the JavaScript function in the web page

Let’s say we have the following code in our web page

[script type=, "text/javascript" [], function, myFunc (), {return, Text, from, web,} [/script]

Native code can call myFunc () as follows

NSString = [self.webView * result stringByEvaluatingJavaScriptFromString:@ "(myFunc)"]; NSLog (@ "% @", result);

The print result is Text from Web

2. page JavaScript call the system’s native code

This step is more complex than the previous one, and iOS, unlike Android, can inject a native code interface directly into the JavaScript function in a web page. Here we will use a more tortuous way to achieve.

Suppose there is a method in the class of Objective-C

- - (void) nativeFunction: (NSString*) args {}

In JavaScript, we use the following method to call the above method at last

Window.JSBridge.callFunction ("callNativeFunction", "some data");

In our pages, there is no JSBridge.callFunction, and this step we have to inject at the native code side.

In webView’s delegate – (void) webViewDidFinishLoad: (UIWebView *) webView, we inject JavaScript in the lower way

NSString *js = "@ (function () {{} = window.JSBridge; window.JSBridge.callFunction = function (functionName, args) {var = URL / bridge-js://invoke / var?"; callInfo = {}; callInfo.functionname = functionName; if (args) {callInfo.args = args;} url = JSON.stringify (callInfo); VAR rootElm = document.documentElement; VAR = document_createElement_x_x_x (iFrame / "IFRAME/"); iFrame.setAttribute ("src/", URL); rootElm.a (iFrame); iFrame.parentNode.removeChild (iFrame);}; return true;}) ([webView; stringByEvaluatingJavaScriptFromString:js]); ";

Simple explanation, first we create an object called JSBridge in window, and then define a method callFunction inside, this method is used to pack two parameters for the JSON string, and then attached to bridge-js://invoke URL our custom? After edge, finally use the IFRAME way to load the URL

The reason for this is that when the loading of IFRAME, it will call the webView delegate – (BOOL) webView: (UIWebView * webView) shouldStartLoadWithRequest: (NSURLRequest *) request navigationType: (UIWebViewNavigationType) navigationType method, the request URL is that we have a custom, this is where we are handled as follows

NSURL *url = [request URL]; NSString *urlStr = url.absoluteString; return [self processURL:urlStr];

The processURL function is as follows

- (BOOL) processURL: (NSString * URL) {NSString *urlStr = [NSString stringWithString:url]; NSString *protocolPrefix = @ "bridge-js://invoke?"; if ([[urlStr lowercaseString] hasPrefix:protocolPrefix]) {urlStr = [urlStr = substringFromIndex:protocolPrefix.length]; urlStr [urlStr stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; NSError *jsonError; NSDictionary *callInfo = [NSJSONSerialization JSONObjectWithData:[urlStr dataUsingEncoding: NSUTF8StringEncoding] options:kNilOptions error:& jsonError] NSString; *functionName [callInfo = objectForKey:@ "functionname"]; NSString * args [callInfo objectForKey:@ = "args"]; if ([functionName "callNat isEqualToString:@ IveFunction "[]) {[self nativeFunction:args];} return NO;} return YES;}

From inside URL bridge-js://invoke? This custom behind them with a JSON string to resolve them, then judge function name key the value of callNativeFunction if nativeFunction calls to native methods, if you need more method calls, as long as add the mapping on the line.

So far, both calls to JavaScript and Objective-C code have been implemented.

thinking

A better JSBridge solution: WebViewJavascriptBridge

React Native


Facebook launched an open source new APP development program that uses JS+’s native native syntax to implement functionality. The bottom layer converts React to native API, and iOS does not share the same set of code as Android, compared to Weex.
, based on React, claims “Learn, once, write, anywhere,”
takes time to prepare for learning…

Weex


Weex comes from the Ali system, and the bottom line is the same as React-Native, which is to render JS code into native components. Based on the
Vue, “Write once, run anywhere, claimed that the”
hesitation in the end learn which
recommended learning Author: @ a wisp of mourning stream hidden half frost