How to prevent WKWebView from repeatedly requesting permission to access a location?

I have a WKWebView in my application, and when I start browsing www.google.com or any other website that requires a location service, a pop-up window appears asking for permission to access the location of the device, even if I have already decided to share my by location.

The only thing I did to manage this location was that I added the NSLocationWhenInUseUsageDescription attribute to my info.plist .

I could not find the answers on the Internet, so any idea would be really appreciated.

+10
source share
2 answers

It turns out that it’s quite difficult, but you can do it. You must inject JavaScript code that intercepts requests to navigator.geolocation and pass them to your application, then get the location using CLLocationManager , and then inject the location back into JavaScript.

Here is a brief outline:

  1. Add WKUserScript to your WKWebView configuration which overrides the navigator.geolocation methods. Embedded JavaScript should look like this:

     navigator.geolocation.getCurrentPosition = function(success, error, options) { ... }; navigator.geolocation.watchPosition = function(success, error, options) { ... }; navigator.geolocation.clearWatch = function(id) { ... }; 
  2. Using WKUserContentController.add(_:name:) add a script message handler to your WKWebView . Embedded JavaScript should call your handler, like this:

     window.webkit.messageHandlers.locationHandler.postMessage('getCurrentPosition'); 
  3. When a webpage requests a location, this method is userContentController(_:didReceive:) so that your application userContentController(_:didReceive:) that the webpage is requesting a location. Find your location using CLLocationManager as usual.

  4. Now it's time to embed the location back into the requesting JavaScript using webView.evaluateJavaScript("didUpdateLocation({coords: {latitude:55.0, longitude:0.0}, timestamp: 1494481126215.0})") . Of course, your didUpdateLocation JavaScript should have a didUpdateLocation function ready to run the saved success handler.

Pretty long algorithm, but it works!

+10
source

Following @AlexanderVasenin's instructions, I created an essence that works great.

Sample code here

Assuming index.html is the page you are trying to load.

  1. Override the navigator.geolocation.getCurrentPosition HTML method that is used to request location information using this script.
  let scriptSource = "navigator.geolocation.getCurrentPosition = function(success, error, options) {window.webkit.messageHandlers.locationHandler.postMessage('getCurrentPosition');};" let script = WKUserScript(source: scriptSource, injectionTime: .atDocumentEnd, forMainFrameOnly: true) contentController.addUserScript(script) 

so whenever a webpage tries to call navigator.geolocation.getCurrentPosition , we redefine it by calling func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage)

  1. The userContentController method then retrieves the location data from the CLLocationManager and calls the method on the web page to process this response. In my case, this is the getLocation(lat,lng) method.

This is the complete code.

ViewController.swift

 import UIKit import WebKit import CoreLocation class ViewController: UIViewController , CLLocationManagerDelegate, WKScriptMessageHandler{ var webView: WKWebView? var manager: CLLocationManager! override func viewDidLoad() { super.viewDidLoad() manager = CLLocationManager() manager.delegate = self manager.desiredAccuracy = kCLLocationAccuracyBest manager.requestAlwaysAuthorization() manager.startUpdatingLocation() let contentController = WKUserContentController() contentController.add(self, name: "locationHandler") let config = WKWebViewConfiguration() config.userContentController = contentController let scriptSource = "navigator.geolocation.getCurrentPosition = function(success, error, options) {window.webkit.messageHandlers.locationHandler.postMessage('getCurrentPosition');};" let script = WKUserScript(source: scriptSource, injectionTime: .atDocumentEnd, forMainFrameOnly: true) contentController.addUserScript(script) self.webView = WKWebView(frame: self.view.bounds, configuration: config) view.addSubview(webView!) webView?.uiDelegate = self webView?.navigationDelegate = self webView?.scrollView.delegate = self webView?.scrollView.bounces = false webView?.scrollView.bouncesZoom = false let url = Bundle.main.url(forResource: "index", withExtension:"html") let request = URLRequest(url: url!) webView?.load(request) } func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { if message.name == "locationHandler",let messageBody = message.body as? String { if messageBody == "getCurrentPosition"{ let script = "getLocation(\(manager.location?.coordinate.latitude ?? 0) ,\(manager.location?.coordinate.longitude ?? 0))" webView?.evaluateJavaScript(script) } } } } 

index.html

 <!DOCTYPE html> <html> <body> <h1>Click the button to get your coordinates.</h1> <button style="font-size: 60px;" onclick="getUserLocation()">Try It</button> <p id="demo"></p> <script> var x = document.getElementById("demo"); function getUserLocation() { if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(showPosition); } else { x.innerHTML = "Geolocation is not supported by this browser."; } } function showPosition(position) { getLocation(position.coords.latitude,position.coords.longitude); } function getLocation(lat,lng) { x.innerHTML = "Lat: " + lat+ "<br>Lng: " + lng; } </script> </body> </html> 
0
source

All Articles