Search code examples
reactjscordovacreate-react-app

Cordova incorrectly resolving paths of image elements constructed during Javascript execution


I'm trying to build a cross platform app with Cordova and React. However I'm having a hard time getting the app to consistently find images from www whose paths are defined in JSX.

Setup

  • I bootstrapped the react app using create-react-app.
  • I have set up my CSP and config.xml as required <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline' 'unsafe-eval' https://fonts.googleapis.com file: data: content; media-src *; img-src * 'self' 'unsafe-inline' 'unsafe-eval' file: data: content:; connect-src https://example.com 'self' ws:; font-src https://fonts.googleapis.com https://fonts.gstatic.com 'self'"> Don't worry, I'll restrict this once I have the app working.

Investigation thus far

  • The app loads external images successfully
  • If I set the same image path in index.html as <img src="static/media/my_image.becd5aba.jpg"/> then the image loads fine.
  • If I import the image and set it as the src in JSX, it doesn't load.
  • If I import the image and trim the leading / in JSX to create a relative path, it doesn't load.
  • If I hardcode the relative path static/media/my_image.becd5aba.jpg in JSX, it doesn't load.
  • Inspecting these two elements using the Safari inspector for my iOS sim gives the following paths

    • Image that displays from index.html:

      • Path defined in index.html markup static/media/my_image.becd5aba.jpg
      • Path found in inspector file:///Users/anthonymanning-franklin/Library/Developer/CoreSimulator/Devices/SOME-BIG-PATH-STUFF/data/Containers/Bundle/Application/MORE-PATH-GUFF/MyApp.app/www/static/media/my_image.becd5aba.jpg
    • Image that doesn't display:

      • Path defined in JSX static/media/my_image.becd5aba.jpg
      • Path rendered at runtime file:///static/media/my_image.becd5aba.jpg

Potential solutions

  1. Does Cordova run a webserver or am I literally browsing the filesystem in the webview? It looks like the issue is to do with relative paths not resolving correctly in the file system context, so it looks at file:///static instead of file://path/to/application/static/.

  2. I've tried the following

    import myImage from './../../assets/my_image.jpg'
    
    let cordova = window.cordova;
    ...
    <img src={`${cordova.file.applicationDirectory}${myImage}`}/>
    

But I get a React error

Attempted to update component V that has already been unmounted (or failed to mount).

I guess I need some sort of effective way of referencing the full path in Javascript at run time.

Similar issues

  • This answer is someone experiencing the same issue, and the accepted answer on this page doesn't work for me.

Any ideas?


Solution

  • Turns out solution #2 was on the right track.

    cordova.file.applicationDirectory gives me the right path to the app root but it required the following steps:

    1. Install the plugin with cordova plugin add cordova-plugin-file
    2. Ensure you have <script type="text/javascript" src="cordova.js"></script> in index.html
    3. Ensure your React code begins after deviceready. In my case I set index.js to the following:

      import React from 'react';
      import ReactDOM from 'react-dom';
      import './css/index.css';
      import App from './App';
      import registerServiceWorker from './registerServiceWorker';
      
      document.addEventListener('deviceready', () => {
        ReactDOM.render(<App />, document.getElementById('root'));
        registerServiceWorker();
      }, false);
      

    Then created a cordova_globals.js file as such:

    let FILE_PATH = '';
    
    document.addEventListener('deviceready', () => {
      FILE_PATH = `${window.cordova.file.applicationDirectory}www`;
    }, false);
    
    export {FILE_PATH};
    

    Now in my JSX I can reference local images as such:

    import {FILE_PATH} from 'cordova_globals.js'
    
    <img src={`${FILE_PATH}${myImage}`}/>