Hybrid.jpg
Objective
How JS in a Web page interacts with iOS Native is a skill that every iOS ape must master. And when it comes to Native and JS interaction, you have to mention a mouth Hybrid.
Hybrid's translation is not very civilized (wipe the sweat, do not know why many translation software will be translated as "hybrid", but I prefer to translate it into "mixed, half-blood"), Hybrid Mobile App I understand it as through WEB network technology (such as HTML,CSS and JavaScript) hybrid mobile applications that are combined with Native.
So let's see what the pros and cons of Hybrid compare Native:
Hybrid_vs_native.jpg
Because the flexibility of Hybrid (changing Web pages does not have to be re-issued) and versatility (a H5 to play across all platforms) plus a low threshold (front apes can be painless to get started), it is possible to use the web in a non-core functional module by Hybrid way to achieve the possibility from all aspects will be better than Nati Ve. Native can provide powerful support for JS on the core function and the device hardware call.
Index
A brief history of Hybrid development
JavaScriptCore Introduction
How IOS Native interacts with JS
The unique method of Wkwebview and JS interaction
JS via Native call IOS device webcam Demo
Summarize
A brief history of Hybrid development
The following is a brief description of Hybrid's history:
1.H5 Release
Html5.png
HTML5 was officially released in September 2014, and one of the biggest changes in this release was "upgrading from a previous subset of XML to a separate collection."
2.H5 infiltrate Mobile App Development
There is a WebView component in Native APP development (Webview,ios has UIWebView and Wkwebview in Android) and this component can load Html files.
Before H5 big line, WebView loaded Web page is very monotonous (because only some static resources can be loaded), since the H5 fire, the front-end ape developed H5 page in the WebView performance is very good, so that the H5 development slowly penetrated into the Mobile App development.
3.Hybrid status
Although there have been RN and Weex these use JS write Native App technology, but Hybrid still not be eliminated, most of the market application has introduced the Web page to a different degree.
JavaScriptCore
JavaScriptCore this library, which Apple added to the standard library after iOS 7, has an epoch-making impact on the iOS Native and JS interaction calls.
JavaScriptCore is roughly comprised of 4 classes and 1 protocols:
Javascriptcore_framework.jpg
Jscontext is the JS execution context, which you can interpret as the JS running environment.
Jsvalue is a reference to JavaScript values, and any value in JS can be packaged as a jsvalue.
Jsmanagedvalue is the packaging of Jsvalue, added "conditional retain".
Jsvirtualmachine represents an isolated environment for JavaScript execution.
There are also Jsexport protocols:
Implements a protocol that exports the Objective-c class and its instance methods, class methods, and properties to JavaScript code.
The jscontext,jsvalue,jsmanagedvalue here is relatively good understanding, the following we put Jsvirtualmachine to illustrate:
The usage of jsvirtualmachine and its relationship with Jscontext
Jsvirtualmachine.jpg
An introduction to the official documentation:
The Jsvirtualmachine instance represents an isolated environment for JavaScript execution. You use this class for two main purposes: to support concurrent JavaScript execution, and to manage the memory of objects bridged between JavaScript and objective-c or Swift.
With regard to the use of jsvirtualmachine, in general we do not have to manually create the Jsvirtualmachine. Because when we get Jscontext, the acquired Jscontext belongs to a jsvirtualmachine.
Each JavaScript context (Jscontext object) belongs to a jsvirtualmachine. Each jsvirtualmachine can contain multiple contexts, allowing values to be passed between contexts (Jsvalue objects). However, each jsvirtualmachine is different, that is, we cannot pass the value created in one jsvirtualmachine to the context in another jsvirtualmachine.
The JavaScriptCore API is thread-safe-for example, we can create Jsvalue objects from any thread or run JS scripts-but all other threads that try to use the same jsvirtualmachine will be blocked. To run JavaScript scripts concurrently (concurrently) on multiple threads, use a separate jsvirtualmachine instance for each thread.
Conversion tables for Jsvalue and JavaScript
IOS Native and JS interaction
For IOS Native and JS interaction we start with the direction of the call in two ways:
JS Call Native
Native Call JS
Call-eachother.jpg
JS Call Native
In fact, JS call IOS Native is also divided into two ways:
False Request method
JavaScriptCore method
False Request method
Principle: In fact, this way is the use of WebView proxy method, in the WebView to start the request to intercept the request, judge whether the request is a good deal of false request. If it is a false request is that JS want to follow the convention call our Native method, according to the Convention to execute our Native code is good.
UIWebView
The UIWebView agent has a function for intercepting the request, and it is good to make a judgment inside:
-(BOOL) WebView: (UIWebView *) WebView shouldstartloadwithrequest: (nsurlrequest *) Request Navigationtype: ( Uiwebviewnavigationtype) Navigationtype {
Nsurl *url = Request. URL;
Comparison with well-appointed function masterpieces
if ([[URL scheme] isequaltostring:@ "Your_func_name"]) {
Just do it
}
}
Wkwebview
Wkwebview has two agents, one is wknavigationdelegate and the other is wkuidelegate. Wkuidelegate We will talk about this in the following chapters, here we need to set up and implement its Wknavigationdelegate method:
-(void) WebView: (Wkwebview *) WebView decidepolicyfornavigationaction: (wknavigationaction *) navigationaction Decisionhandler: (void (^) (wknavigationactionpolicy)) Decisionhandler {
Nsurl *url = NavigationAction.request.URL;
Comparison with well-appointed function masterpieces
if ([[URL scheme] isequaltostring:@ "Your_func_name"]) {
Just do it
Decisionhandler (Wknavigationactionpolicycancel);
Return
}
Decisionhandler (Wknavigationactionpolicyallow);
}
Note:decisionhandler is the code block to invoke when your application decides whether to allow or cancel navigation. The code block uses a single parameter, which must be one of the constants of the enumeration type Wknavigationactionpolicy. If you do not call Decisionhandler it will cause crash.
Add the JS code here:
function callnative () {
Loadurl ("your_func_name://xxx");
}
Then take a button tag and use it for a moment:
<button type= "button" onclick= "Callnative ()" >call native!</button>
JavaScriptCore method
IOS 7 has javascriptcore specifically designed to do Native and JS interactions. We can get jscontext after WebView finishes loading, and then use Jscontext to refer to the object in JS to interpret or respond to it using the Native code:
First introduce the JavaScriptCore library
#import <JavaScriptCore/JavaScriptCore.h>
And then UIWebView to finish loading the proxy method
-(void) Webviewdidfinishload: (UIWebView *) WebView {
Get JS Context
Jscontext = [WebView valueforkeypath:@ "DocumentView.webView.mainFrame.javaScriptContext"];
To make a reference, the elements in the JS reference to explain, such as the method can be interpreted as Block, the object can also point to OC Native object Oh
jscontext[@ "iosdelegate"] = self;
jscontext[@ "Yourfuncname"] = ^ (id parameter) {
Note that the thread here is the Web processing thread by default, and if the main thread is involved, you need to go to the main thread manually
Dispatch_async (Dispatch_get_main_queue (), ^{
Your code
});
}
}
And JS side of the code is simpler, simply declare a non-explanatory function (the name of the contract), used to give Native reference:
var parameter = xxx;
Yourfuncname (parameter);
IOS Native Call JS
The implementation of the IOS Native call JS is also divided by JavaScriptCore:
WebView directly into JS and execute
On the IOS platform, WebView has an API to inject and execute JS.
UIWebView
UIWebView has the method of directly injecting JS:
NSString *jsstr = [NSString stringwithformat:@ "Showalert ('%@ ')", @ "alert msg"];
[_webview STRINGBYEVALUATINGJAVASCRIPTFROMSTRING:JSSTR];
Note: This method returns the result of running JS (nullable NSString *), which is a synchronous method that blocks the current thread! Although this method is not deprecated, it is a best practice to use the evaluateJavaScript:completionHandler:method of the Wkwebview class.
Official documents:
The Stringbyevaluatingjavascriptfromstring:method waits synchronously for JavaScript evaluation to complete. If you load Web content whose JavaScript code that has not vetted, the invoking this method could the hang your app. Best Practice is to adopt the Wkwebview class and use its evaluateJavaScript:completionHandler:method instead.
Wkwebview
The method that differs from Uiwebview,wkwebview injection and execution of JS does not block the current thread. Because the JS code in the Web content that WebView loads is not necessarily verified, the blocking thread may suspend the APP.
NSString *jsstr = [NSString stringwithformat:@ "setlocation ('%@ ')", @ "nan gu gong xiang hannaford hu Tong xx, Dongcheng District, Peking City"];
[_webview evaluatejavascript:jsstr completionhandler:^ (id _nullable result, nserror * _nullable error) {
NSLog (@ "%@----%@", result, error);
}];
Note: The method does not block the thread, and its callback code block always runs in the main thread.
Official documents:
Evaluates a JavaScript string.
The method sends the result of the script evaluation (or an error) to the completion handler. The completion handler always runs on the main thread.
JavaScriptCore method
The above simply mentions the Jsvalue class provided by the JavaScriptCore Library, which provides an introduction to Jsvalue in the official documentation:
A Jsvalue instance is a reference to a JavaScript value. You can use the Jsvalue class to convert basic values (such as numbers and strings) between JavaScript and Objective-c or Swift to pass data between native code and JavaScript code.
But you also saw the OC and JS data type conversion tables that I posted above, which are not limited to the basic values stated in the official documentation. If you are unfamiliar with JS, I will explain why Jsvalue can also point to the object and function JS, because the JS language does not distinguish between basic values and objects and functions, in JS "Everything is the object".
Okay, here's the direct show Code:
First introduce the JavaScriptCore library
#import <JavaScriptCore/JavaScriptCore.h>
Get the JS context first
Self.jscontext = [WebView valueforkeypath:@ "DocumentView.webView.mainFrame.javaScriptContext"];
If the UI action is involved, the tangent back to the main thread calls the Yourfuncname in the JS code, via the array @[parameter] into the parameter
Dispatch_async (Dispatch_get_main_queue (), ^{
Jsvalue *jsvalue = self.jscontext[@ "Yourfuncname"];
[Jsvalue Callwitharguments:@[parameter]];
});
The above code calls the Yourfuncname function in the JS code, and adds @[parameter] as the entry parameter to the function. In order to facilitate reading comprehension, here again paste the JS code:
function Yourfuncname (arguments) {
var result = arguments;
Do the u want to do
}
The unique method of Wkwebview and JS interaction
Wkwebview.jpg
The difference between Wkwebview and UIWebView is not explained in detail in this article, and more information is also available for your own reference. This is about Wkwebview's unique approach when interacting with JS:
Wkuidelegate method
MessageHandler method
Wkuidelegate method
For Wkwebview mentioned above, in addition to Wknavigationdelegate, it also has a wkuidelegate, what is this wkuidelegate for?
The Wkuidelegate protocol contains functions that are used to listen for Web JS when you want to display alert or confirm. If we load a Web in Wkwebview and want the Web JS alert or confirm to pop up properly, we need to implement the corresponding proxy method.
Note: If the corresponding proxy method is not implemented, WebView will follow the default action to make the behavior.
Alert:if implement this method, the Web view would behave as If the user selected the OK button.
Confirm:if implement this method, the Web view would behave as If the user selected the Cancel button.
We take the alert example here, and I believe you can extrapolate your readers. Here are the chestnuts in the proxy method for wkuidelegate listening Web to display alert using the Native Uialertcontroller instead of the alert display in JS:
-(void) WebView: (Wkwebview *) WebView runjavascriptalertpanelwithmessage: (NSString *) message initiatedbyframe: ( Wkframeinfo *) frame Completionhandler: (void (^) (void)) Completionhandler {
Use Native's Uialertcontroller pop-up window to display the information JS will prompt
Uialertcontroller *alert = [Uialertcontroller alertcontrollerwithtitle:@ "Reminder" Message:message PreferredStyle: Uialertcontrollerstylealert];
[Alert addaction:[uialertaction actionwithtitle:@ "Know" Style:uialertactionstylecancel handler:^ (UIAlertAction * _ Nonnull action) {
Completionhandler must be called within the function
Completionhandler ();
}]];
[Self Presentviewcontroller:alert animated:yes completion:nil];
}
MessageHandler method
MessageHandler is a Native intercept JS false request after another kind of JS call Native method, the method takes advantage of the new features of Wkwebview implementation. Compared with the method of intercepting false Request, the MessageHandler parameter is more simple and convenient.
What does MessageHandler mean?
The Wkusercontentcontroller class has a method:
-(void) Addscriptmessagehandler: (id <WKScriptMessageHandler>) scriptmessagehandler name: (NSString *) name;
This method is used to add a script processor, can be processed within the processor to the JS script call method, so as to achieve the purpose of JS call Native.
So Wkusercontentcontroller and Wkwebview have a hairy relationship?
In the initialization function of Wkwebview, there is an entry configuration, whose type is wkwebviewconfiguration. Wkwebviewconfiguration contains an attribute Usercontentcontroller, the Usercontentcontroller is an instance of the Wkusercontentcontroller type, We can use this usercontentcontroller to add a different name to the script processor.
Wkusercontentcontroller.jpg
MessageHandler's pit.
So back to-(void) Addscriptmessagehandler:name: Method Above, this method adds a script message handler (the first entry parameter Scriptmessagehandler), and gives the processor a name (the second entry parameter Name). However, this function in use when there is a pit: Scriptmessagehandler into the participants are strongly quoted, then if you wkwebview the current Uiviewcontroller as the first entry, this Viewcontroller Webview.configuration by his own possession. Usercontentcontroller, it will cause a circular reference.
Retaincycle.jpg
We can delete a strong reference to Viewcontroller by using the-(void) Removescriptmessagehandlerforname: method. So in general our code will be added and removed in Viewwillappear and viewwilldisappear MessageHandler:
-(void) Viewwillappear: (BOOL) Animated {
[Super viewwillappear:animated];
[Self.webview.configuration.userContentController addscriptmessagehandler:self name:@ "Yourfuncname"];
}
-(void) Viewwilldisappear: (BOOL) Animated {
[Super viewwilldisappear:animated];
[Self.webview.configuration.userContentController removescriptmessagehandlerforname:@ "Yourfuncname"];
}
Wkscriptmessagehandler protocol
Wkscriptmessagehandler is a script information processor protocol that must be adhered to if you want an object to have script-handling capabilities (such as the self of the above code, WebView, which belongs to Viewcontroller above).
The Wkscriptmessagehandler protocol is very simple inside, there is only one way we have to implement this method (@required):
Wkscriptmessagehandler protocol method, triggering when a script message is received
-(void) Usercontentcontroller: (Wkusercontentcontroller *) Usercontentcontroller didreceivescriptmessage: ( Wkscriptmessage *) Message {
Message has two properties: Name and Body
Message.name can be used to distinguish between the processing to be done
if ([Message.name isequaltostring:@ "Yourfuncname"]) {
Message.body equivalent to JS passed over the parameters
NSLog (@ "JS Call native Success%@", message.body);
}
}
Add the JS code:
<name> Change yourfuncname,<messagebody> for the entry you want
Window.webkit.messagehandlers.<name>.postmessage (<messageBody>)
This article transferred from: HTTP://WWW.JIANSHU.COM/P/5329170BE7B3
Interactive summary of iOS and JS development