First, the way to intercept URL requests

1. Determine the URL to be intercepted with the front end in advance, and implement the proxy method of UIWebViewshouldStartLoadWithRequestIntercepts implementation-defined urls in methods, intercepts post-processing native logic, and callbacks.

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    if ([request.URL.absoluteString hasSuffix:@"js_native://alert"]) {/ / this is bounced [self alertWidthValue: request. URL. AbsoluteString]; / / this is the callback [webView stringByEvaluatingJavaScriptFromString: @"JsCallBackNativeMethod (' I'm a callback ')"];
    }
    returnYES; } H5 code <! DOCTYPE html> <html lang="en">
    <head>
        <meta charset="UTF-8"< <title> JS and native interaction </title> </head> <body> <h1> JS and native interaction call native shell </h1> <button onclick="jsCallNativeMethod()" style="border: 1px solid black"</button> </body> </ HTML > <script>function jsCallNativeMethod() {// You can pass the parameter to spell after the line location.href ="js_native://alert"; } // native callback js methodsfunction jsCallBackNativeMethod(arguments) {
        alert('Native call js method pass argument ='+ arguments); } < / script > native callback js [self. WebView stringByEvaluatingJavaScriptFromString: @"JsCallBackNativeMethod (' I'm a callback ')"];
Copy the code

Use JavaScriptCore

Procedure: 1. Specify parameters to be passed, method names, and callback methods with the front end. 2. Implement webViewDidFinishLoad proxy method, get JS, write in the implementation to call the native method, receive the passed parameters. Use evaluateScript to perform a callback to HTMLCopy the code

Implementation method one: directly obtain the method defined in JS

- (void)webViewDidFinishLoad:(UIWebView *)webView{
    JSContext *jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; __weak typeof(self) weakSelf = self; // The method defined by nativeAlert js jsContext[@"nativeAlert"] = ^(){// Incoming parameters NSArray *arguments = [JSContext currentArguments]; NSLog(@"% @",arguments);
        NSString *value = [NSString stringWithFormat:@"% @",[arguments objectAtIndex:0]]; [self alertWidthValue:value]; [weakSelf handleOtherOperating]; }; } - (void)handleOtherOperating {// other processing NSLog(@"Thread = %@", [NSThread currentThread]); // Native callback js method JSContext * context = [_webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    [context evaluateScript:@"NativeCallbackJscMothod (' I'm a parameter passed natively ')"]; } h5 code <! DOCTYPE html> <html lang="en">
    <head>
        <meta charset="UTF-8"< <title> JS and native interaction </title> </head> <body> <h1> JS and native interaction call native shell </h1> <button onclick="jscCallNativeMethod()" style="border: 1px solid black"</button> </body> </ HTML > <script>function jscCallNativeMethod() {
        nativeAlert('I'm passing the parameter 1'.'I am passing the parameter 2'); } // The native callback method can receive arguments that are nativefunction nativeCallbackJscMothod(arguments) {
        alert('Native call js method pass argument =' + arguments);
    }
</script>

Copy the code

Implementation method two: use JSExport protocol to process

Steps: 1. In the HTML page to define the need to call the native method, native callback JS method 2. Create a utility class that implements a protocol that complies with JSExport and provides methods that JS needs to call 3. Expose this class to HTML using JSContext in webViewDidFinishLoadCopy the code
// Create a utility class that implements a protocol that complies with JSExport and provides methods that JS needs to call#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>NS_ASSUME_NONNULL_BEGIN creates a protocol that follows the JSExport. @protocol JSToolProtocol <NSObject,JSExport> // Provide native methods for JS calls, and expose some attributes if you want. - (void)jsCallNativeMethod; @end @interface JSTool : NSObject<JSToolProtocol> @property (nonatomic, strong) JSContext * jsContext; @end NS_ASSUME_NONNULL_END @implementation JSTool - (void)jsCallNativeMethod { NSLog(@"Js calls native methods"); [self. JSContext evaluateScript:@] // If you want to call the js method, you need to get the webView's JSContext [self. JSContext evaluateScript:@]"Native VecallBackjscmothod (' nativeCallbackJscMothod ')"]; } @end <! DOCTYPE html> <html lang="en">
    <head>
        <meta charset="UTF-8"< <title> JS and native interaction </title> </head> <body> <h1> JS and native interaction call native shell </h1> <button onclick="jseCallNativeMethod()" style="border: 1px solid black"</button> </body> </ HTML > <script>function jseCallNativeMethod() { jsTool.jsCallNativeMethod(); } // Native callback methods can accept arguments that are passed nativefunction nativeCallbackJscMothod(arguments) {
        alert('Native call js method pass argument =' + arguments);
    }
</script>


- (void)webViewDidFinishLoad:(UIWebView *)webView{
    JSContext *jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; JSTool *jsTool = [[JSTool alloc]init]; jsTool.jsContext = jsContext; // jsContext[@"jsTool"] = jsTool; // Method two [jsContextsetObject:jsTool forKeyedSubscript:@"jsTool"];
}

Copy the code

Third, through WKWebView

* WKWebViewConfiguration is used to initialize WKWebView configuration. WKPreferences Specifies whether the webView can use JAVASCRIPT or other plug-ins. WKUserContentController is used to configure the code for JS interaction UIDelegate is used to control the display of some popovers in WKWebView (Alert, Confirm, prompt). * WKNavigationDelegate is used to monitor the load of the web page, including whether the load is allowed, load failed, load succeeded, etc.Copy the code
/ / intercept URL - (void) webView: (WKWebView *) webView decidePolicyForNavigationAction: (navigationAction WKNavigationAction *) decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { NSURL * url = navigationAction.request.URL; NSString * scheme = url.scheme; NSString * query = url.query; NSString * host = url.host;if ([[url absoluteString] hasSuffix:@"js_native://alert"]) {
        [self handleJSMessage];
        decisionHandler(WKNavigationActionPolicyCancel);
        return; } decisionHandler(WKNavigationActionPolicyAllow); } - (void)handleJSMessage {// callback JS method [_wkWebView evaluateJavaScript:@"nativeCallbackJscMothod('123')" completionHandler:^(id _Nullable x, NSError * _Nullable error) {
        NSLog(@"x = %@, error = %@", x, error.localizedDescription);
    }];
}
#pragma mark - WKUIDelegate// Call alert() on the JS side of the alert() method to trigger the following method, And pass the message to alert information - (void) webView: (WKWebView *) webView runJavaScriptAlertPanelWithMessage message: (nsstrings *) initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler { UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"Warm reminder" message:message preferredStyle:UIAlertControllerStyleAlert];
    [alert addAction:[UIAlertAction actionWithTitle:@"Sure" style:UIAlertActionStyleCancel handler:nil]];
    [self presentViewController:alert animated:YES completion:nil];
    completionHandler();
}

- (WKWebView *)wkWebView{
    if(! _wkWebView) {// Initialize WKWebViewConfiguration * Configuration = [[WKWebViewConfiguration alloc] init]; The configuration/JS/configuration interaction code. UserContentController = [WKUserContentController new]; WKPreferences * Preferences = [WKPreferences new]; preferences.javaScriptCanOpenWindowsAutomatically = YES; Preferences. MinimumFontSize = 50.0; configuration.preferences = preferences; _wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(_webView.frame), [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height/2) configuration:configuration]; _wkWebView.navigationDelegate = self; _wkWebView.UIDelegate = self; [self.view addSubview:_wkWebView]; }return_wkWebView; } <! DOCTYPE html> <html lang="en">
    <head>
        <meta charset="UTF-8"< <title> JS and native interaction </title> </head> <body> <h1> JS and native interaction call native shell </h1> <button onclick="jsCallNativeMethod()" style="border: 1px solid black"</button> </body> </ HTML > <script>function jsCallNativeMethod() {// You can pass the parameter to spell after the line location.href ="js_native://alert"; } // The native callback method can receive arguments that are nativefunction nativeCallbackJscMothod(arguments) {
        alert('Native call js method pass argument =' + arguments);
    }
</script>



Copy the code

WKScriptMessageHandler

WKWebView does not support JavaScriptCore. At this point we can use WKWebView's WKScriptMessageHandler. 1. Use userContentController in native code to add native methods that need to be called on the JS side 2. Implement WKScriptMessageHandler protocol in the only method 'didReceiveScriptMessage' 3. In this method, message.name is used to get the name of the called method, and the parameter 4 passed by the JS side is obtained through message.body. On the JS end through ` window. Its. MessageHandlers. MethodName (set name.) postMessage (['name'.'parameters'.'age', 18]) 'didReceiveScriptMessage' is received by the WK method and can be obtained by 'message.body'.Copy the code

1. ByinitWithFrame:configurationInitialization method, giveconfigurationThe incomingWKWebViewConfigurationIn object,WKWebViewConfigurationThe configuration andJSInteractive methods.

- (WKWebView *)wkWebView{
    if(! _wkWebView) {// Initialize WKWebViewConfiguration * Configuration = [[WKWebViewConfiguration alloc] init]; The configuration/JS/configuration interaction code. UserContentController = [WKUserContentController new]; / / MessageHandler add object Remember to achieve agreement < WKScriptMessageHandler > name JS send postMessage object [configuration. UserContentController addScriptMessageHandler:self name:@"jsCallNativeMethod"];
        _wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(_webView.frame), [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height/2) configuration:configuration];
        _wkWebView.navigationDelegate = self;
        _wkWebView.UIDelegate = self;
        [self.view addSubview:_wkWebView];
        
    }
    return _wkWebView;
}
Copy the code

2. Implement the protocol and implement itdidReceiveScriptMessageProxy method when js is calledjsCallNativeMethodMethod is called back to the proxy method.

#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    if ([message.name isEqualToString:@"jsCallNativeMethod"] {// This is the argument NSLog(@"% @",message.body); // Call back the JS method [_wkWebView evaluateJavaScript:@"nativeCallbackJscMothod('123')" completionHandler:^(id _Nullable x, NSError * _Nullable error) {
//        NSLog(@"x = %@, error = %@", x, error.localizedDescription); }]; }}Copy the code

3. Js calls

/ / the window. Its. MessageHandlers. < name >. PostMessage (< messageBody >) / / the name is the second parameter setting MessageHandlerfunction jsCallNativeMethod() {
        window.webkit.messageHandlers.jsCallNativeMethod.postMessage('My parameters');
    }
Copy the code

4. Remove

- (void)dealloc {// To avoid circular references, the controller cannot be released. Need to remove the [self. WebView. Configuration. UserContentController removeScriptMessageHandlerForName: @"jsCallNativeMethod"];
}
Copy the code

Fifth, through the third-party libraryWebViewJavascriptBridge

WebViewJavascriptBridge address

Current project code address