Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

File Download and Access Issues on iOS 17.5.1 with Cordova #628

Open
andreafrancioni opened this issue Jul 11, 2024 · 8 comments
Open

File Download and Access Issues on iOS 17.5.1 with Cordova #628

andreafrancioni opened this issue Jul 11, 2024 · 8 comments
Labels

Comments

@andreafrancioni
Copy link

andreafrancioni commented Jul 11, 2024

Versions:

•	iOS Version: 17.5.1
•	Cordova Version: 12.0.0 ([email protected])
•	Cordova Plugin File Version: 8.1.0

Description:

I am experiencing an issue with downloading and accessing media files on iOS 17.5.1 using Cordova. The code works perfectly on Android devices, but on iOS, the files are downloaded but cannot be accessed or played.

Steps to Reproduce:

1.	Prompt the user to download media files.
2.	Download the files and save them in the cache directory using cordova.file.dataDirectory.
3.	Attempt to access and play the downloaded files.

Current Behavior:

On iOS, the files are downloaded successfully, but when attempting to access or play these files, the app fails to locate the files, and they appear to be missing or inaccessible.

Expected Behavior:

The downloaded files should be accessible and playable from the cache directory, similar to the behavior observed on Android devices.

Relevant Code:

setDialog(
"E' necessario scaricare i contenuti audio/video, vuoi continuare?",
"yes-no",
(answer) => {
if (answer === "yes") {
this.downloadMediaFiles();
}
}
);

getCacheDirectoryPath() {
// Ottieni il percorso della directory di cache
window.resolveLocalFileSystemURL(cordova.file.dataDirectory, (dirEntry) => {
this.cacheDirectory = dirEntry.toURL();
console.log("Cache directory: " + this.cacheDirectory);
}, (error) => {
console.error("Errore nel risolvere il file system: " + error.toString());
});
}

async downloadMediaFiles() {
this.downloadProgress = 0;
this.downloadMessage = Scaricamento di 0/${this.mediaFiles.length} file;

for (let i = 0; i < this.mediaFiles.length; i++) {
await this.downloadFile(this.server_base_path + this.mediaFiles[i]);
this.downloadProgress = ((i + 1) / this.mediaFiles.length) * 100;
this.downloadMessage = Scaricamento di ${i + 1}/${this.mediaFiles.length} file;
}

this.downloadMessage = "Download completato!";
localStorage.setItem('downloadedMediaFiles', JSON.stringify(this.mediaFiles));

setTimeout(() => {
this.showDownloadDialog = false;
}, 300);
}

async downloadFile(url) {
console.log("Download URL:", url);
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const blob = await response.blob();
const fileName = url.split('/').pop();
console.log("File name:", fileName);

window.resolveLocalFileSystemURL(this.cacheDirectory, (dirEntry) => {
  dirEntry.getFile(fileName, { create: true, exclusive: false }, (fileEntry) => {
    fileEntry.createWriter((fileWriter) => {
      fileWriter.onwriteend = () => {
        console.log("Download completato: " + fileEntry.toURL());
      };
      fileWriter.onerror = (error) => {
        console.error("Errore nel download del file: " + error.toString());
      };
      console.log(blob);
      fileWriter.write(blob);
    }, (error) => {
      console.error("Errore nella creazione del file: " + error.toString());
    });
  }, (error) => {
    console.error("Errore nel recuperare il file: " + error.toString());
  });
}, (error) => {
  console.error("Errore nel risolvere il file system: " + error.toString());
});

} catch (error) {
console.error("Errore nel download del file: " + error.toString());
}
}

video source are build like this:
<video preload="auto" playinline="" autoplay="" controls="" controlslist="nofullscreen"><source src="file:///var/mobile/Containers/Data/Application/(APP_ID)/Library/NoCloud/(FILENAME).mp4" type="video/mp4"></video>

Observations:

•	The issue appears to be specific to iOS.
•	The file download and access process works correctly on Android devices.
•	When attempting to download a file through Safari’s debugger, it can be accessed correctly if saved manually.

Possible Causes:

•	Differences in file system access permissions between iOS and Android.
•	Possible bug in cordova-plugin-file or Cordova itself on iOS 17.5.1.

Any insights or suggestions on how to resolve this issue would be greatly appreciated. Specifically, if there are known issues with cordova-plugin-file on iOS 17.5.1 or if additional configurations are required, please let me know.

@phynae
Copy link

phynae commented Aug 1, 2024

Same here! Working when reading and writing using the FileSystem-API but not when passing the file-url to for example a HTML-ImageTag. Furthermore my attempt was to convert it using the window.WkWebView.convertFilePath.

@phynae
Copy link

phynae commented Aug 2, 2024

The following scenarios were tested, but the issue persisted:

  • Older Cordova-Lib version ([email protected])
  • Older ios-platform version ([email protected])
  • Older cordova-plugin-file version (7.0.0)
  • Deploying older apps from XCode on an iPhone
  • Older iOS Version (iOS 12)

Workaround (not recommended though):

  • Converting the file into a Data-URL and then putting this url in the src-Tag of an image

Weird thing is, that when downloaded the already released version of the same app (release was about half a year ago), the issue is gone

It almost seems like it has to do with X-Code. Unfortunately it was not possible for me to check that.

@breautek
Copy link
Contributor

breautek commented Aug 9, 2024

If you're app is configured to use schemes (e.g. your html page is loaded over http(s)://... on android or app://... (the scheme is customizable on iOS) then using file:// paths in your DOM will likely get blocked by CORs. If you're app is loaded over the file:// protocol, then using file:// paths in the DOM should work as expected but is considered less secure.

v7 (if I recall correctly) of the file plugin introduce changes to produce a DOM-usable url when using the FileEntry.toURL() method.

The pasted code is not well formatted and is hard to read but here are some things to check:

  1. Make sure .toURL() is being used.
  2. The ionic webview may not parse the URL produced by .toURL() properly, so if the ionic webview is being used, attempt to test using the default cordova webview.

If it still doesn't work, please let us know what version of cordova-ios you're using and whether you're using schemes or loading directly the file:// protocol.

@phynae
Copy link

phynae commented Aug 12, 2024

Hi, we are using [email protected]. I've already tried using the .toURL function without success. If your assumption that the change was introduced in v7 is true, then i wonder why it didn't work when i build using an older version.

Furthermore, as mentioned above, we faced the same error when re-deploying old apps (that previously had no such issues) that use way older versions of cordova-ios onto iPhones running a pretty modern iOS-Version.

@dele1907
Copy link

I also have the problem which @phynae mentioned above.
Passing the file URL to an image tag is not working for me, but when I access files for reading or writing using the filesystem-API everything works fine.

I am operating on [email protected] with [email protected].
Additionally I have tried to get rid of the issue using older versions of [email protected], [email protected] and [email protected] but this will not change anything.
Moreover the .toURL function also did not help me to solve the problem.

@breautek breautek added bug and removed info-needed labels Aug 27, 2024
@breautek
Copy link
Contributor

I was able to take a deeper look this morning and .toURL() on file entries is suppose to produce a DOM-usable url, but it won't on ios when using app schemes.

It's missing an implementation override the scheme task and to map a scheme URL to the file on disk to return an asset over the scheme.

Workarounds could include reading the asset as a blob and using blob urls, or if the asset is small enough, read as a data uri. Don't forget that blob urls needs to be released when it's not being used anymore.

If you're using a large asset like a video file, then there is no "good" workaround available afaik.

I can't really give any timeline on when a proper solution will be made available but it is on our radar now.

@andreafrancioni
Copy link
Author

Hi breautek, its been long time since last time i tried using cached file on ios, there's been new implementation or have you implemented new functionality about this problem?

Thank you for your patience

@breautek
Copy link
Contributor

breautek commented Oct 29, 2024

This would be specific to iOS but I've been told that since the addition of schemes on iOS there is a WkWebView.convertFilePath API available in JS.

It's not ideal since you would have to do a platform check but you could try it as a work around.

I'm not familiar with the API, but I believe it would work something like:

let myImage = document.getElementById('myImage');
myImage.src = WkWebView.convertFilePath(theFilePath);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants