iosreact-nativepermissionsgeolocationreact-native-webview

How to pass geolocation permission to react-native-webview?


When using the geolcoation api in a react-native-webview, I am asked twice whether the app is allowed to use my current location. How is it possible to forward the already (not) granted permission to the webview?

I am currently using react-native 0.68 and react-native-webview 11.22.

First prompt:

first promt

Second prompt:

enter image description here

I should only be asked once for permission to use my current geolocation.


Solution

  • In case somebody faces the same problem, I solved this issue with the following workaround. I injected a custome javacscript into the webview to override the used geolocation api in the webview. My custome script does all the communication with the app. The app returns the geolocation and so the webview doesn't need to aks for permission.

    Custome Script

    export const getGeoLocationJS = () => {
      const getCurrentPosition = `
        navigator.geolocation.getCurrentPosition = (success, error, options) => {
          window.ReactNativeWebView.postMessage(JSON.stringify({ event: 'getCurrentPosition', options: options }));
    
          window.addEventListener('message', (e) => {
            let eventData = {}
            try {
              eventData = JSON.parse(e.data);
            } catch (e) {}
    
            if (eventData.event === 'currentPosition') {
              success(eventData.data);
            } else if (eventData.event === 'currentPositionError') {
              error(eventData.data);
            }
          });
        };
        true;
      `;
    
      const watchPosition = `
        navigator.geolocation.watchPosition = (success, error, options) => {
          window.ReactNativeWebView.postMessage(JSON.stringify({ event: 'watchPosition', options: options }));
    
          window.addEventListener('message', (e) => {
            let eventData = {}
            try {
              eventData = JSON.parse(e.data);
            } catch (e) {}
    
            if (eventData.event === 'watchPosition') {
              success(eventData.data);
            } else if (eventData.event === 'watchPositionError') {
              error(eventData.data);
            }
          });
        };
        true;
      `;
    
      const clearWatch = `
        navigator.geolocation.clearWatch = (watchID) => {
          window.ReactNativeWebView.postMessage(JSON.stringify({ event: 'clearWatch', watchID: watchID }));
        };
        true;
      `;
    
      return `
        (function() {
          ${getCurrentPosition}
          ${watchPosition}
          ${clearWatch}
        })();
      `;
    };

    Webview

    import Geolocation from '@react-native-community/geolocation';
    
    let webview = null;
    
    <WebView
      geolocationEnabled={ true }
      injectedJavaScript={ getGeoLocationJS() }
      javaScriptEnabled={ true }
      onMessage={ event => {
        let data = {}
        try {
          data = JSON.parse(event.nativeEvent.data);
        } catch (e) {
          console.log(e);
        }
    
        if (data?.event && data.event === 'getCurrentPosition') {
          Geolocation.getCurrentPosition((position) => {
            webview.postMessage(JSON.stringify({ event: 'currentPosition', data: position }));
          }, (error) => {
            webview.postMessage(JSON.stringify({ event: 'currentPositionError', data: error }));
          }, data.options);
        } else if (data?.event && data.event === 'watchPosition') {
          Geolocation.watchPosition((position) => {
            webview.postMessage(JSON.stringify({ event: 'watchPosition', data: position }));
          }, (error) => {
            webview.postMessage(JSON.stringify({ event: 'watchPositionError', data: error }));
          }, data.options);
        } else if (data?.event && data.event === 'clearWatch') {
          Geolocation.clearWatch(data.watchID);
        } 
      }}
      ref={ ref => {
        webview = ref;
        if (onRef) {
          onRef(webview)
        }
      }}
      source={ url }
      startInLoadingState={ true }
    />