I have a WKWebView in my app and when I start browsing www.google.com or any other website that requires location service, a pop up window appears, asking for the permission to access the location of the device even if I have already accepted to share my location.
The only thing I did to manage this location stuff is that I added the NSLocationWhenInUseUsageDescription attribute in my info.plist.
I couldn't find any answers on the web so any idea would be really appreciated.
Turns out it's quite hard, but possible to do. You have to inject JavaScript code which intercepts requests to navigator.geolocation and transfer them to your app, then get the location with CLLocationManager, then inject location back to the JavaScript.
Here is the brief scheme:
Add
WKUserScriptto yourWKWebViewconfiguration which overrides methods ofnavigator.geolocation. Injected JavaScript should look like this:navigator.geolocation.getCurrentPosition = function(success, error, options) { ... }; navigator.geolocation.watchPosition = function(success, error, options) { ... }; navigator.geolocation.clearWatch = function(id) { ... };With
WKUserContentController.add(_:name:)add script message handler to yourWKWebView. Injected JavaScript should call your handler, like this:window.webkit.messageHandlers.locationHandler.postMessage('getCurrentPosition');When a web page will request a location, this method will fire
userContentController(_:didReceive:)so your app would know web page is requesting location. Find your location with the help ofCLLocationManageras usual.Now it's time to inject the location back to the requesting JavaScript with
webView.evaluateJavaScript("didUpdateLocation({coords: {latitude:55.0, longitude:0.0}, timestamp: 1494481126215.0})"). Of course your injected JavaScript should havedidUpdateLocationfunction ready to launch saved success handler.
Quite a long algorithm, but it works!
So following the steps outlined by @AlexanderVasenin, I created a gist which works perfectly.
Assuming index.html is the page you're trying to load.
- Override the HTML method
navigator.geolocation.getCurrentPositionwhich is used to request for location info with 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 the webpage tries to call navigator.geolocation.getCurrentPosition, we override it by calling func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage)
- the
userContentControllermethod then gets the location data fromCLLocationManagerand calls a method in the webpage to handle that response. In my case, the method isgetLocation(lat,lng).
This is the full 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>
来源:https://stackoverflow.com/questions/39665367/how-to-prevent-wkwebview-to-repeatedly-ask-for-permission-to-access-location