iOS Hybrid Development Evolution

Native Web technology is there relative to many advantages, such as: Cross-end (browser, Android, iOS), more flexible layout, with immediate effect and so on. So, in development we often use some Web pages embedded in APP.

Thus, the introduction of interactive web and Native, often JavaScript is interacting with the Native.

JS interact with the Native, can be roughly divided into: Native call JS, JS call Native.

Native call JS

Native JS simple call

//UIWebView
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;

//WKWebView
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;
复制代码

note:

  • UIWebView JS execution is synchronous, asynchronous in WKWebView in.
  • Return value UIWebView supports only the basic types of String, does not support the type of collection (Array, Dictionary) and the object.
  • WKWebView perform JS callback Block in support collections and objects, and also supports basic types of String.
h5 Code
<!DOCTYPE html>
<html>
<head>
	<title>WKWebView</title>
    <meta charset="UTF-8" />
	
    <script type="text/javascript">
        function methodA(username, password) {
            console.log("Hello, I am methodA");
            let dict = {
                "username" : username,
                "password" : password
            };
            return "username: " + username + ", password: " + password;
            <!--return dict;-->
            <!--return ["A", "B"];-->
        }
	</script>
</head>
<body>
</body>
</html>
复制代码
Native Code
//UIWebView
NSString *str = [self.webView stringByEvaluatingJavaScriptFromString:@"methodA('zhangsan', '123456')"];
NSLog(@"str");

//WKWebView
[self.webView evaluateJavaScript:@"methodA('zhangsan', '123456')" completionHandler:^(id _Nullable value, NSError * _Nullable error) {
    if (error) {
        NSLog(@"%@", error);
        return ;
    }
    NSLog(@"%@", value);
}];
复制代码

JS call Native

In fact, JS call Native gone through several stages of interaction.

Before iOS7

Before iOS7, JS loaded want to take the initiative to trigger a new url when called Native. We intercept the URL to determine whether to call the native function in this way to deal with JS calls to the Native agreed in advance by agreement.

JS mainly through the end window.location.assignto trigger a new load.

In iOS - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationTypefor the URL to make judgments.

JS code
<!DOCTYPE html>
<html>
<head>
	<title>WKWebView</title>
	<meta charset="UTF-8" />
	<script type="text/javascript">
        function callNative() {
            window.location.assign("native://func1?name=zhangsan&password=123456");
        }
	</script>
</head>
<body>
	<button onClick="callNative()">CallNative</button>
</body>
</html>
复制代码
OC Code
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    NSString *urlPath = [request.URL absoluteString];
    /*
     * 与Web端协商定义交互协议 scheme://function_name?para1=value1&para2=value2&para3=value3&callback=callback_value
     *  scheme为native时,是要与原生进行交互。
     *  function_name指定要调用原生的什么功能。
     *  para1是参数名称,value1是参数值
     *  callback是原生执行完后对js的回调
     */
    if ([urlPath hasPrefix:@"native://"]) {
        urlPath = [urlPath substringFromIndex:@"native://".length];
        NSArray *questionMarkArray = [urlPath componentsSeparatedByString:@"?"];
        NSString *funcName;
        NSArray *paraArray;
        if (questionMarkArray.count > 0) {
            funcName = [questionMarkArray firstObject];
            if (questionMarkArray.count > 1) {
                // 从问号后还是都认定为参数
                NSString *paraStr =  [urlPath substringFromIndex:funcName.length + 1];
                paraArray = [paraStr componentsSeparatedByString:@"&"];
            }
        }
        
        // funcname 匹配
        if ([funcName isEqualToString:@"func1"]) {
            
        } else if ([funcName isEqualToString:@"func2"]) {
            
        } else if ([funcName isEqualToString:@"func3"]) {
            
        } else {
            
        }
        
        return NO;
    } else {
        return YES;
    }
}
复制代码

iOS7

iOS7 start, the system disclosed JavaScriptCore framework, based on this we can interact with Natvie of JS.

Javascript debugging method of insertion (we can debug JS code on the Mac).

Safari's preferences checked in at the bottom in the menu bar display "development" menu , menu bar, Safari and the more out development menu, when after completion of loading webview h5 may be selected corresponding to the application under the corresponding simulator web debugging. Detailed steps and real machine debugging method of your own Google.

iOS7 in the manner commonly used to get JSContext, OC and then injected into the subject, or a method in JS.

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    self.jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    
    // JS的异常回调
    self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exception) {
        NSLog(@"%@", exception);
    };
    
    // 注入方法
    self.jsContext[@"addNum"] = ^(int a, int b) {
        return a + b;
    };
    
    // 注入对象
    NSDictionary *dict = @{@"aaa" : @"aaa", @"bbb" : @"bbb"};
    self.jsContext[@"dict"] = [JSValue valueWithObject:dict inContext:self.jsContext];
}
复制代码

Then in JS can invoke a corresponding object and method, consistent with the method is called and calling the object or native.

function callByContext() {
    <!-- addNum为原生向JS注入的方法 -->
    var num = addNum(5, 8);
    console.log(num);
    <!-- dict为原生向JS注入的对象 -->
    console.log(dict);
}
复制代码

iOS8 WKWebView

UIWebView have a lot of performance problems, so Apple introduced a new browser component WKWebView in iOS8.

And after iOS12 Apple abandoned UIWebView, so be sure to ask someone to quickly upgrade to WKWebView! ! !

In the interactive WKWebView, you should use WKUserContentController. When WKWebView build, we need to pass WKWebViewConfiguration, and WKWebViewConfiguration can add WKUserContentController as ScriptMessageHandler.

WKUserContentController *userContent = [[WKUserContentController alloc] init];
//伪代码
[userContent addScriptMessageHandler:id<WKScriptMessageHandler> name:@"MyNative"];
    
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.userContentController = userContent;
    
WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config];
复制代码

WKScriptMessageHandler protocol handler object needs to achieve, by the end JS when window.webkit.messageHandlerssending message Native protocol handler object method is invoked by the protocol process parameters by value.

#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {}
复制代码

JS method call iOS

// Javascript
function callNative() {
  window.webkit.messageHandlers.MyNative.postMessage('body');
}
复制代码

JS and iOS-related code has been uploaded to GitHub ( github.com/NewFarmer21... ), free to download and debug.

Reproduced in: https: //juejin.im/post/5d06fca4518825498b57dff0

Guess you like

Origin blog.csdn.net/weixin_33795833/article/details/93177478