This is the 10th day of my participation in the Gwen Challenge.More article challenges

preface

Earlier, I explained the interaction between iOS native, Flutter and H5. Today, I will introduce the interaction between iOS and H5 through a third-party framework.

This third party framework is not new, old, in the ERA of UIWebView, I used it for OC and JS intertune work. It is WebViewJavascriptBridge

Here, LET me give you a taste of it:

Gitignore was founded eight years ago.

The last update was in November 2017, which is 4-5 years from now.

I personally think there are two reasons why this framework has not been updated for so long and is still available:

  • 1. After the launch of WKWebView, Apple did not launch any new modules and components for Web. Although WKWebView will add some new apis with the iteration of iOS version, the core interaction core and business of iOS and H5 remain unchanged.

  • 2. WebViewJavascriptBridge by Objective – C language to write, so the biggest characteristic is the basic won’t have any language update, very stable.

For these two reasons, this is why WebViewJavascriptBridge is still available.

The use of WebViewJavascriptBridge

This article will attach the example. HTML provided by the official WebViewJavascriptBridge to enable JS to call the native camera, obtain the image data, and actively call the JS method to transfer the image data to the JS side of the code and examples.

First on the JS side of the code, important places I will write comments, please look carefully, in addition, I asked my front-end partner, said that this code is relatively ancient, we mainly see

inside the logic implementation.

Html side code:

<! Doctype HTML > < HTML > <head> <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, Word-wrap: break-word! Important; "> < span style =' text-align: left; color:#222; } h1 { color:steelblue; font-size:24px; margin-top:24px; } button { margin:0 3px 10px; font-size:12px; } .logLine { border-bottom:1px solid #ccc; padding:4px 2px; font-family:courier; font-size:11px; } </style> </head> <body> <h1>WebViewJavascriptBridge Demo</h1> <div id="images"></div> <script> window.onerror = function(err) { log('window.onerror: ' + err) } function setupWebViewJavascriptBridge(callback) { if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); } if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); } window.WVJBCallbacks = [callback]; var WVJBIframe = document.createElement('iframe'); WVJBIframe.style.display = 'none'; WVJBIframe.src = 'https://__bridge_loaded__'; document.documentElement.appendChild(WVJBIframe); setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0) } setupWebViewJavascriptBridge(function(bridge) { var uniqueId = 1 function log(message, data) { var log = document.getElementById('log') var el = document.createElement('div') el.className = 'logLine' el.innerHTML = uniqueId++ + '. ' + message + ':<br/>' + JSON.stringify(data) if (log.children.length) { Log.insertbefore (el, log.children[0])} else {log.appendChild(el)}} Bridge. RegisterHandler ('nativeImageData', function(data, ResponseCallback ('Swift call JS nativeImageData with', data) let imageString = data.imgData var d1=document.getElementById("images") var img=document.createElement("img") img.src= "data:image/png; base64,"+imageString d1.appendChild(img) var responseData = { 'JS':'Get the Image' } log('JS responding with', responseData) responseCallback(responseData) }) document.body.appendChild(document.createElement('br')) var callbackButton = document.getElementById('buttons').appendChild(document.createElement('button')) InnerHTML = 'callBackButton. onclick = function(e) {e.preventDefault() log('JS calls Swift') // JsCallNativeTakePhoto: jsCallNativeTakePhoto: jsCallNativeTakePhoto: jsCallNativeTakePhoto: jsCallNativeTakePhoto bridge.callHandler('jsCallNativeTakePhoto', {'message': 'take a photo'}, function(response) { log('JS got response', response) }) } }) </script> <div id='buttons'></div> <div id='log'></div> </body> </html>Copy the code

The iOS side code

Import UIKit import WebKit import AVFoundation import WebViewJavascriptBridge Private Var Bridge: WebViewJavascriptBridge! Class BridgeWebViewController: UIViewController {/// private lazy var webView: WKWebView = { let config = WKWebViewConfiguration() let preferences = WKPreferences() preferences.javaScriptCanOpenWindowsAutomatically = true config.preferences = preferences let webView = WKWebView(frame:  view.frame, configuration: config) webView.allowsBackForwardNavigationGestures = true webView.navigationDelegate = self return webView }() override  func viewDidLoad() { super.viewDidLoad() bridgeSetting() setupUI() } } extension BridgeWebViewController { func SetupUI () {view.backgroundcolor =.white view.addSubView (webView) /// Call the local HTML let htmlPath = Bundle.main.path(forResource: "ExampleApp", ofType: "html") let appHtml = try! String(contentsOfFile: htmlPath! , encoding: .utf8) let baseURL = URL(fileURLWithPath: htmlPath!) webView.loadHTMLString(appHtml, baseURL: BaseURL)} / / / bridge configuration func bridgeSetting () {if let _ = bridge. {return} WebViewJavascriptBridge enableLogging () / / / Bridge = WebViewJavascriptBridge(forWebView: WebView) / / / agents set up bridge. SetWebViewDelegate (self) / / / registration by bridge to monitor the handle jsCallNativeTakePhoto of JS Bridge. RegisterHandler ("jsCallNativeTakePhoto") {[weak self] any, responseCallback in // In the native side method implementation /// here I do two things: 1. Print ("jsCallNativeTakePhoto:\(any)") responseCallback? Guard let self = self else {return} guard let self = self else {return} self.showSystemImageAlertControll(delegate: Self)}} // Use the bridge to call the function on the JS side and pass the native data to the private func takeImageToJS(imageBase64String: String, type: Int) { bridge.callHandler("nativeImageData", data: ["imgData": imageBase64String, "type": Type])}} / / / UIImagePickerControllerDelegate callback image data extension BridgeWebViewController: UIImagePickerControllerDelegate { func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { picker.dismiss(animated: true) } func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {/ / / image binary guard let image = info [UIImagePickerController. InfoKey. OriginalImage] as? UIImage, Let data = image.pngData() else {return} /// binary to base64 string let string = data.base64encodedString () Picker. Dismiss (animated: true) {self.takeImageToJS(imageBase64String: String, type: 0)}}} the extension BridgeWebViewController: UINavigationControllerDelegate {} / / / to monitor the WebView to load the life cycle, according to the need to use, just wrote the two here. Extension BridgeWebViewController: WKNavigationDelegate {/// call /// /// -parameters when the page starts loading: /// -webview: // navigation func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {} /// after the page is loaded, call /// /// -parameters: /// -webview: implement the webView of the proxy /// -navigation: Current navigation func webView(_ webView: WKWebView, didFinish Navigation: WKNavigation!) {}}Copy the code

Both native and JS have the same two methods, registerHandler and callHandler, respectively, to register listener and call, equivalent to ensure the consistency of the Api at each end, parallel through the callback, You can return a message received from the other side through a callback.


WebViewJavascriptBridge does not have much to explain in terms of usage. The native side and the JS side agree on a good method name, input parameter form, you can debug.

Yes on AndroidJsBridgeCommunicate with H5, for iOS, Android, H5 in the method level of the great unity.

WebViewJavascriptBridge

As mentioned above, WebViewJavascriptBridge was last updated in 2017, and the code in it is compatible with UIWebView, which is currently expired on iOS and will be rejected when it is used.

It is recommended to delete the part of WebViewJavascriptBridge that involves UIWebView before integration.

This framework is also a good example of calling OC code in Swift.

Finally attached call system camera and photo album UIViewController classification, CV use

Import UIKit import AVFoundation / / / agreement system album call public typealias SystemImagePickerInterface = UIImagePickerControllerDelegate & UINavigationControllerDelegate // MARK: - System album call UIViewController {//MARK:- Show alertController, album selection, photo, cancel method /// show alertController /// /// - Parameter delegate: need to set up the agent class public func showSystemImageAlertControll (delegate: SystemImagePickerInterface) { let imagePicker = UIImagePickerController() imagePicker.navigationBar.barTintColor = navigationController? .navigationBar.barTintColor imagePicker.navigationBar.tintColor = navigationController? .navigationBar.tintColor imagePicker.navigationBar.titleTextAttributes = navigationController? .navigationBar.titleTextAttributes imagePicker.delegate = delegate imagePicker.allowsEditing = true let alertController = UIAlertController(title: nil, message: nil, preferredStyle: ActionSheet) // Take a photo let takePhotonAction = UIAlertAction(title: "Take a photo ", style:.default) {(action: UIAlertAction) in self.takePhoto(imagePicker: ImagePicker)} / / from selected album let selectFromPhotoAlbumAction = UIAlertAction (title: "choose from photo album," style: the default) {(action: UIAlertAction) in the self. SelectFromPhotoAlbum (imagePiker: imagePicker)} / / cancel the let cancelAction: UIAlertAction = UIAlertAction(title: "cancel ", style:.cancel, handler: nil) alertController.addAction(takePhotonAction) alertController.addAction(selectFromPhotoAlbumAction) alertController.addAction(cancelAction) present(alertController, animated: true, completion: Nil)} //MARK:- select the specific method of image /// take photos to obtain the method of image /// /// -parameter imagePicker: UIImagePickerController private func takePhoto(imagePicker: UIImagePickerController) { imagePicker.sourceType = .camera useFrontOrRearCamera(imagePiker: imagePicker) let status: AVAuthorizationStatus = AVCaptureDevice.authorizationStatus(for: AVMediaType.video) switch status { case .authorized: present(imagePicker, animated: true, completion: nil) case .denied: break case .restricted: break case .notDetermined: present(imagePicker, animated: true, completion: nil) @unknown default: /// /// -parameter imagePiker: UIImagePickerController private func selectFromPhotoAlbum(imagePiker: UIImagePickerController) { imagePiker.sourceType = .photoLibrary present(imagePiker, animated: true, completion: /// /// -parameter imagePiker: int imagePiker: int imagePiker: int imagePiker: int imagePiker UIImagePickerController private func useFrontOrRearCamera(imagePiker: UIImagePickerController) { if UIImagePickerController.isCameraDeviceAvailable(.rear) { imagePiker.cameraDevice = .rear }else if UIImagePickerController.isCameraDeviceAvailable(.front) { imagePiker.cameraDevice = .front }else { return } } }Copy the code

Tomorrow to continue

Tomorrow is Friday, and the Dragon Boat Festival is coming soon. This week, I will introduce Swift and H5 interaction for my work. Next, I will introduce network request modules, which are Alamofire, Moya, and Codable.

Enumerations and other small features might be covered before we get to that.

Come on, everybody!