Interaction between iOS and JS (ObjC version)

Reprinted in: iOS Development Technology Sharing
Preface

ObjectiveC and Js interaction is a common requirement, but for novices or so-called experts, it is not so simple and clear. Only the JavaScriptCore framework that came out after iOS7.0 is introduced here.

About

the types of JavaScriptCore involved in this tutorial:

JSContext, JSContext represents the execution environment of JS, through the -evaluateScript: method, you can execute a JS code
JSValue, JSValue encapsulates the corresponding types in JS and ObjC, and calls JS API, etc.
JSExport, JSExport is a protocol, we can define our own protocol by complying with this protocol, and the API declared in the protocol will be exposed in JS, in order to call
ObjC and JS interaction mode

Through JSContext, we have two kinds of The method of calling JS code:

1. Call the JS code directly
2. Inject the model through JSContext in ObjC, and then call the method of the model Call the JS code
directly
// A JSContext object, similar to the window in Js,
 // only needs to be created once.
 self.jsContext = [[JSContext alloc] init];

 // jscontext can directly execute JS code.
 [self.jsContext evaluateScript:@"var num = 10"];
 [self.jsContext evaluateScript:@"var squareFunc = function(value) { return value * 2 }"];
 // Calculate the area of ​​the square
 JSValue *square = [self.jsContext evaluateScript:@"squareFunc(num)"];

 // You can also get the method by subscripting
 JSValue *squareFunc = self.jsContext[@"squareFunc"];
 JSValue *value = [squareFunc callWithArguments:@[@"20"]];
 NSLog(@"%@", square.toNumber);
 NSLog(@"%@", value.toNumber);

This way there is no model injected into JS. This method is not suitable to use. Usually, there are many global functions in JS. In order to prevent the same name, the model method is the best. Through the model name we negotiated, the API exposed by the model we defined in ObjC is called directly through the model in JS.
Interaction by injecting models

First , we need to define a protocol, and this protocol must comply with the JSExport protocol.
@protocol JavaScriptObjectiveCDelegate <JSExport>

// JS calls this method to call the system album method of OC
- (void)callSystemCamera;
// When called in JS, the function name should be showAlertMsg(arg1, arg2)
// There are only two parameters here.
- (void)showAlert:(NSString *)title msg:(NSString *)msg;
// Pass it in via JSON
- (void)callWithDict:(NSDictionary *)params;
// JS calls Oc, and then passes values ​​to JS by calling JS methods in OC.
- (void)jsCallObjcAndObjcCallJsWithDict:(NSDictionary *)params;

@end


Next, we also need to define a model:

// This model is used to inject JS into the model so that methods can be called through the model.
@interface HYBJsObjCModel : NSObject <JavaScriptObjectiveCDelegate>

@property (nonatomic, weak) JSContext *jsContext;
@property (nonatomic, weak) UIWebView *webView;

@end

Implement this model:

@implementation HYBJsObjCModel

- (void)callWithDict:(NSDictionary *)params {
 NSLog(@"Js called the OC method, the parameters are: %@", params);
}

// Js calls callSystemCamera
- (void)callSystemCamera {
 NSLog(@"JS called the OC method to call up the system album");

 // After JS calls OC, JS is called through OC, but this is not passed parameters
 JSValue *jsFunc = self.jsContext[@"jsFunc"];
 [jsFunc callWithArguments: nil];
}

- (void)jsCallObjcAndObjcCallJsWithDict:(NSDictionary *)params {
 NSLog(@"jsCallObjcAndObjcCallJsWithDict was called, params is %@", params);

 // call the JS method
 JSValue *jsParamFunc = self.jsContext[@"jsParamFunc"];
 [jsParamFunc callWithArguments:@[@{@"age": @10, @"name": @"lili", @"height": @158}]];
}

- (void)showAlert:(NSString *)title msg:(NSString *)msg {
 dispatch_async(dispatch_get_main_queue(), ^{
   UIAlertView *a = [[UIAlertView alloc] initWithTitle:title message:msg delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
   [a show];
 });
}

@end


Next, we inject the model into the JS in the proxy where the webview is loaded in the controller.

#pragma mark - UIWebViewDelegate
- (void)webViewDidFinishLoad:(UIWebView *)webView {
 self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
 // Calling the method via the model is better.
  HYBJsObjCModel *model  = [[HYBJsObjCModel alloc] init];
 self.jsContext[@"OCModel"] = model;
 model.jsContext = self.jsContext;
 model.webView = self.webView;

 self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
   context.exception = exceptionValue;
   NSLog(@"Exception information: %@", exceptionValue);
 };
}

We get it through valueForKeyPath of webView, whose path is documentView.webView.mainFrame.javaScriptContext.
In this way, we can get the context of JS, and then inject our model object into this context.

Let's start by writing two JS methods:

var jsFunc = function() {
   alert('Objective-C call js to show alert');
 }

 var jsParamFunc = function (argument) {
   document.getElementById('jsParamFuncSpan').innerHTML
   = argument['name'];
 }

Here we define two JS methods, one is jsFunc with no parameters.
The other is jsParamFunc, which takes one parameter.

Next, we add the following code to the body in the html:

<div style="margin-top: 100px">
<h1>Test how to use objective-c call js</h1>
<input type="button" value="Call ObjC system camera" onclick="OCModel.callSystemCamera()">
<input type="button" value="Call ObjC system alert" onclick="OCModel.showAlertMsg('js title', 'js message')">
</div>

<div>
<input type="button" value="Call ObjC func with JSON " onclick="OCModel.callWithDict({'name': 'testname', 'age': 10, 'height': 170})">
<input type="button" value="Call ObjC func with JSON and ObjC call js func to pass args." onclick="OCModel.jsCallObjcAndObjcCallJsWithDict({'name': 'testname', 'age': 10, 'height': 170})">
</div>

<div>
<span id="jsParamFuncSpan" style="color: red; font-size: 50px;"></span>
</div>

The code is now ready to be tested.

When we click the first button: Call ObjC system camera,
through OCModel.callSystemCamera(), we can call the OC method through JS in HTML.
In the OC code, the following two lines of code are added to our callSystemCamera method body, that is, to get the JS defined in the HTML, go to jsFunc, and then call it.

JSValue *jsFunc = self.jsContext[@"jsFunc"];
 [jsFunc callWithArguments: nil];

In this way, when JS calls OC methods, OC can also be fed back to JS.

Take a look at the following dictionary parameters:
- (void)jsCallObjcAndObjcCallJsWithDict:(NSDictionary *)params {
 NSLog(@"jsCallObjcAndObjcCallJsWithDict was called, params is %@", params);

 // call the JS method
 JSValue *jsParamFunc = self.jsContext[@"jsParamFunc"];
 [jsParamFunc callWithArguments:@[@{@"age": @10, @"name": @"lili", @"height": @158}]];
}

Get the jsParamFunc method we defined in HTML, and call it passing a dictionary as a parameter.

Well, that's all, if you want the demo source code, please go to
github: https://github.com/CoderJackyHuang/IOSCallJsOrJsCallIOS

Want to learn the Swift version? Please read:
http://mp.weixin.qq.com/s?__biz=MzIzMzA4NjA5Mw==&mid=214070747&idx=1&sn=57b45fa293d0500365d9a0a4ff74a4e1#rd

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326846961&siteId=291194637