react-native-webview/react-native-webview

Variables and functions from injectedJavaScript not available on Android

svdo opened this issue ยท 6 comments

svdo commented

Bug description:
On Android, variables and functions declared in injectedJavaScript are not available in code run by injectJavaScript, whereas on iOS they are. Please note that I'm using source={{ html: ... }}, not loading an external URI. I believe the behaviour on iOS is correct, that on Android is not.

To Reproduce:
I created a repo that demonstrates this issue. I was careful to create a descriptive git history, so I hope it is pretty self-explanatory: https://github.com/svdo/VarIssue. This repo contains a complete react native project that you should be able to run yourself (make sure Android emulator is running):

yarn
yarn react-native start --reset-cache
yarn react-native run-ios
yarn react-native run-android

Environment:

  • OS: Android 9 and iOS 11
  • react-native-webview version: 5.8.1
  • react-native info:
    React Native Environment Info:
      System:
        OS: macOS 10.14.4
        CPU: (12) x64 Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
        Memory: 18.87 MB / 16.00 GB
        Shell: 5.3 - /bin/zsh
      Binaries:
        Node: 11.14.0 - /var/folders/0d/5_t6qk7s06qf94jnypzwh6yh0000gp/T/yarn--1557086445011-0.06815149805556375/node
        Yarn: 1.15.2 - /var/folders/0d/5_t6qk7s06qf94jnypzwh6yh0000gp/T/yarn--1557086445011-0.06815149805556375/yarn
        npm: 6.9.0 - ~/.homebrew/bin/npm
      SDKs:
        iOS SDK:
          Platforms: iOS 12.2, macOS 10.14, tvOS 12.2, watchOS 5.2
      IDEs:
        Xcode: 10.2.1/10E1001 - /usr/bin/xcodebuild
      npmPackages:
        react: 16.8.3 => 16.8.3
        react-native: 0.59.5 => 0.59.5
    

Note

  • Using injectJavaScript to declare the variables and function, instead of injectedJavaScript, will cause everything to work as expected. This is demonstrated in commit svdo/VarIssue@72f3fa0.
  • This may or may not be related to issue #445.

Hello ๐Ÿ‘‹, this issue has been opened for more than 2 months with no activity on it. If the issue is still here, please keep in mind that we need community support and help to fix it! Just comment something like still searching for solutions and if you found one, please open a pull request! You have 7 days until this gets closed automatically

I have the same problem right now. Did you find a solution or workaround for this?

svdo commented

@its-wufu I have not further pursued this. I noticed this comment the other day though, it may be the workaround you're looking for (but I didn't try):

#445 (comment)

I couldn't make this work unfortunatly. Maybe I show what I did, so someone may find something to add here.

I created a Test App and just used Test Credentials from WireCard. This is how my App.js looks like:

import React, {useRef, useEffect, useState} from 'react';
import {StyleSheet, View, Text, SafeAreaView, Button} from 'react-native';
import {WebView} from 'react-native-webview';
import base64 from 'base-64';
import uuid from 'uuid';

import INJECTED_JAVASCRIPT from './paymentPage';

export default function App() {
  const webViewRef = useRef();

  // Beispiel: https://wpp-test.wirecard.com/seamless?wPaymentToken=emRvVkFXAdge7xs2mr0C21mPO_Bf3INlsO2p4uRFSsc
  const [redirectUrl, setRedirectUrl] = useState(null);

  useEffect(() => {
    const url = 'https://wpp-test.wirecard.com/api/payment/register';
    const data = {
      payment: {
        'merchant-account-id': {
          value: 'cad16b4a-abf2-450d-bcb8-1725a4cef443',
        },
        'request-id': uuid(),
        'transaction-type': 'authorization-only',
        'requested-amount': {
          value: 0,
          currency: 'EUR',
        },
        'payment-methods': {
          'payment-method': [
            {
              name: 'creditcard',
            },
          ],
        },
      },
      options: {
        mode: 'seamless',
        'frame-ancestor': 'https://example.com',  // not sure what to put here either...
      },
    };
    getData(url, data);
  }, []);

  const getData = (url, data) => {
    fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization:
          'Basic ' + base64.encode('70000-APILUHN-CARD:8mhwavKVb91T'),
      },
      body: JSON.stringify(data),
    })
      .then(res => res.json())
      .then(resJson => {
        if (resJson['payment-redirect-url']) {
          setRedirectUrl(resJson['payment-redirect-url']);
          console.log(resJson['payment-redirect-url']);
        } else {
          console.log(resJson);
        }
      });
  };

  const onSubmit = () => {
    // https://github.com/react-native-community/react-native-webview/issues/554
    webViewRef.current.injectJavaScript(`
        WPP.seamlessSubmit({
            onSuccess: function (response) { 
                window.ReactNativeWebView.postMessage('success'); 
                window.ReactNativeWebView.postMessage(response);
            },
            onError: function (response) {
                window.ReactNativeWebView.postMessage('failed');
                window.ReactNativeWebView.postMessage(response);
            }
        });
    `);
  };

  const onMessage = event => {
    console.log('onMessage:');
    console.log(event.nativeEvent.data);
  };

  return (
    <SafeAreaView style={styles.container}>
      {redirectUrl ? (
        <View style={{flex: 1}}>
          <WebView
            ref={webViewRef}
            onMessage={onMessage}
            javaScriptEnabledAndroid={true}
            textZoom={100}
            mixedContentMode={'compatibility'}
            originWhitelist={['*']}
            injectedJavaScript={INJECTED_JAVASCRIPT}
            source={{
              uri: redirectUrl,
            }}
          />
          <Button onPress={onSubmit} title="Submit" />
        </View>
      ) : (
        <Text>Loading...</Text>
      )}
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    marginTop: 30,
  },
});

The Payment Page is from https://wpp-test.wirecard.com/loader/paymentPage.js and I made a String out of it to insert it into the injectedJavaScript property.

Should this not work? Any ideas? Maybe this issue should be opened again...

I solved this by using the onLoad prop.
I load the following function into onLoad:

  const injectJSFileFromWeb = () => {
    webViewRef.current.injectJavaScript(
      `var corescript = document.createElement('script');
        corescript.type = 'text/javascript';
        corescript.src = "https://url/to/code.js";
        var parent = document.getElementsByTagName('head').item(0);
        parent.appendChild(corescript);
        console.log('loaded...')`,
    );
  };

I red that you should care about the linebreaks.

Using this method I can access variables when calling the injectJavaScript method.

ccfz commented

This is still a problem on version: 9.1.4. I could not try 9.2.0 because of another known build error. Once the fix for 9.2.0 is released I try it as well. But for the moment the workaround that @svdo posted above worked for me!