cordova-ios
cordova-ios copied to clipboard
In Cordova app, physical iPhone SE can't display images from "file://" URLs; OK in iOS simulator and Android
Issue Type
- [x] Bug Report
- [ ] Feature Request
- [x] Support Question
Description
UPDATE 3: I managed to work around this using blob:
URLs as generated from URL.createObjectURL()
. See the first comment below.
UPDATE 2: I've upgraded Xcode to 13.4, and iOS to 15.5, and still see the problem. Can anyone else reproduce this problem using the app code listed below?
UPDATE: Just updated cordova-plugin-file to the brand new 7.0.0, but the problem still remains.
Images with "file:" URL don't load in app on physical iPhone SE (2022), but load fine on Android and in the iOS simulator. The test case below tries to set document.body.style.background
, but setting (new Image).src
doesn't work either; the image's onload()
never runs. After setting document.body.style.background
, getComputedStyle(document.body).backgroundImage
shows the correct URL it was set to (as parsed from the background property), but the image never appears.
I understand that on a physical iPhone, the various paths are changed every install due to the new ID in the paths, but the test case below accommodates that, so I don't think that's the problem.
Content-Security-Policy
is set to be permissive (see HTML file below), and includes file:
and others in default-src
, img-src
, and style-src
. (Is there perhaps some system-wide CSP that overrides that?)
This is for user-submitted images, so it has to be under cordova.file.dataDirectory
, not under a read-only directory.
Information
I wrote this post on stackoverflow about this problem, but am including all the details here too.
The only plugin I'm using is cordova-plugin-file
7.0.0 .
Command or Code
The test case here was created as a brand new Cordova project.
The config.xml
of the test case:
<?xml version='1.0' encoding='utf-8'?>
<widget id="io.cordova.hellocordova" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<name>HelloCordova</name>
<description>Sample Apache Cordova App</description>
<author email="[email protected]" href="https://cordova.apache.org">
Apache Cordova Team
</author>
<content src="index.html" />
<allow-intent href="http://*/*" />
<allow-intent href="https://*/*" />
<platform name="android">
<preference name="AndroidXEnabled" value="true" />
<preference name="AndroidInsecureFileModeEnabled" value="true" />
</platform>
<platform name="ios">
<preference name="allowFileAccessFromFileURLs" value="true" />
<preference name="allowUniversalAccessFromFileURLs" value="true" />
</platform>
</widget>
index.html
is:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy"
content="default-src * file: filesystem: ; img-src * file: filesystem: ; style-src * file: filesystem: 'unsafe-eval' ;">
<meta name="viewport" content="initial-scale=1, width=device-width, viewport-fit=cover">
<link rel="stylesheet" href="css/index.css">
<title>Hello World</title>
<script src="cordova.js"></script>
<script src="js/index.js"></script>
</head>
<body>
<h1>Background image should show up</h1>
</body>
</html>
js/index.js
is:
// Wait for the deviceready event before using any of Cordova's device APIs.
document.addEventListener('deviceready', onDeviceReady, false);
async function onDeviceReady() {
try{
// this block just copies file from under cordova.file.applicationDirectory
// to under cordova.file.dataDirectory, for this demo
// FileEntry.copyTo() throws error on ios if file already exists, so use try/catch
try {
let entry= await new Promise((resolve, reject) =>
resolveLocalFileSystemURL(cordova.file.applicationDirectory + 'www/img/home_bg.jpg',
resolve, reject) ) ;
let dest_dir= await new Promise((resolve, reject) =>
resolveLocalFileSystemURL(cordova.file.dataDirectory, resolve, reject) ) ;
await new Promise((resolve, reject) =>
entry.copyTo(dest_dir, undefined, resolve, reject) ) ;
} catch(e) {}
// set full background property
let full_url= cordova.file.dataDirectory + 'home_bg.jpg' ;
document.body.style.background= 'url(' + full_url + ') center/cover' ;
// this shows the correct 'url("file:///Users/.../Library/NoCloud/home_bg.jpg")'
alert('computed backgroundImage=[' + getComputedStyle(document.body).backgroundImage + ']') ;
} catch(e) {
alert('in odr(): '+e) ;
}
}
css/index.css
is simply:
body {
height:100vh;
width:100vw;
}
h1 {
font-size:24pt;
margin:3rem;
text-align:center;
}
Finally, I have a background image in img/home_bg.jpg
that is copied over to cordova.file.dataDirectory
. After this copy, I can read the file correctly from cordova.file.dataDirectory
.
Environment, Platform, Device
I'm developing on a Mac mini (2020) with the M1 chip, running MacOS 12.3 . I'm trying to run the app on a new iPhone SE (2022).
Version information
cordova 11.0.0 cordova-ios 6.2.0 cordova-plugin-file 7.0.0 Mac mini (2020) with M1 chip running MacOS 12.3 iPhone SE (2022) with iOS 15.4 Xcode 13.3
Checklist
- [x] I searched for already existing GitHub issues about this
- [x] I updated all Cordova tooling to their most recent version
- [x] I included all the necessary information above
I managed to work around this by using blob:
URLs for all displayed images:
let blob= new Blob([await read_file_binary(file_entry)]) ;
img.src= URL.createObjectURL(blob) ;
... where read_file_binary()
reads a file as an arrayBuffer. Note that the Content Security Policy must include blob:
in the img-src
directive.
This isn't as efficient as just using file:
URLs, but it's much better than using data:
URLs. It does seem like a bug in either cordova-ios
(?) or in WKWebView (?), but I'm set for now.
I managed to fix a similar error with this snippet
if (device.platform == 'iOS'){ var loc = window.WkWebView.convertFilePath(store + bannerDir); }
I guess this might work for your let full_url
or just install the ionic webview plugin which provides a way to access files using the same url scheme as the app uses working around this problem.