20

We have a fairly complex JS webapp that loads different resources (HTML templates, extra JS/CSS files, JSON data, images, videos, sounds) in different ways (string, arrayBuffer, base64, binary) online in the browser (just reading no writing). Now we'd like to make changes to it to have everything work offline (resources included) on different platforms as a Cordova mobile app.


Problem #1 - loading files from the local filesystem

It turns XHR requests for local file:// resources work everywhere but with Chrome which is a show stopper now since Android is internally transitioning towards Chrome.

Ok, there were still 2 other options left:

  1. fake-load everything within JSONP with script tags, HTML with iframes, etc. - which is not a really nice option because it requires changes to the resources themselves + its kind of hacky and it costs us a lot of functionality (string load only for JS/HTML, arrayBuffer, binary, etc.)
  2. use the File API

Plain vanilla File API unfortunately always requires user interaction (like a click on an input box) to get a file reference plus IE10 is a no-no. Okay, so the only option there is to use the customized File API plugin for PhoneGap - which brings us the next problem.


Problem #2 - (cross-platform?) file paths in PhoneGap

I like PhoneGap CLI - you enter a few commands in the terminal and it manages files and builds for different platforms for you. Or does it?

As far as I can tell according to PhoneGap you're supposed to put everything in the WWW folder - however (once you build the app), the crazy thing is - there is no easy way to read those files (via File API plugin) from where PhoneGap puts them.

You'd think that fileSystem.root (LocalFileSystem) is pointing to that folder, but at least on iOS the WWW folder is inside your app but the fileSystem.root reference is pointing to an external Documents folder (still have to test this on Android but I'm guessing its again something different there).

After a bit of tinkering I figured out how to get to the WWW folder but its unflexible, hacky and feels like it shouldn't work (iOS only at the moment):

window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fileSystem){
   fileSystem.root.getDirectory("../HelloWorld.app/www", {create: true}, function(entry) { 
      entry.getFile("test.json", {create: false}, function(fileEntry) {
         fileEntry.file(function(file){
            var reader = new FileReader();
            reader.onloadend = function(evt) {
               console.log(evt.target.result);
            };
            reader.readAsText(file);             
         }, fail);
      }, fail);
   });
}, fail);

This cannot be the best way to do this. If nothing else, the JS code now needs to now the name of the app to be able to access the WWW folder (BTW is there a system shortcut for this or a system variable that would make accessing the app's folder generic?).

But this would again be something different on other platforms anyway.

I could theoretically "download" the images from the internal WWW folder to the external Documents folder but:

  1. download a 50mb app and then internally copy 40mb of its resources to another folder just so that it can be accessed properly doesn't seem that proper to me
  2. is Documents the proper root folder (on iOS) at all?

Problem #3 - best way to organize local resources in PhoneGap?

So why is /Documents the filesystem root in PhoneGap at all? According to Apple's File System Programming Guide Documents folder might even not be a best fit for something like this. It seems that Documents should be used for user-generated content only and app's data files/resources should in fact go into '/Library' folder. This is amongst other things important since some apps seem to get rejected by Apple for doing this wrong + this also of course influences iTunes/cloud backups.


So now that we've established all of that:

  • how do you structure local files/resources properly in PhoneGap?
  • where/how do I put our resources (JS, JSON, HTML, CSS, media, etc.) so that they could be copied/installed to a proper folder for every platform (like /Library or ok even /Documents on iOS - but not remain app internal). And of course also for other platforms.
  • how to access all of it properly and cross-platform?
  • (side question) is cordova.js really needed? we've worked really hard to exclude external libraries and vanilla optimize performance so I'm not too crazy about including another one to again take over events and different things. I realize that there might be some internal hooks used here - but maybe we could just include ourselves what we need?

This turned out to be way more complex than I though it would be.

And as much as I tried not to and I appreciate the effort lots of people have put into PhoneGap - my mind kept drawing parallels between all this illogical cross-platform behavior and browser wars a decade ago. In my mind, other than displaying browser apps full screen cross-platform - the most important feature of PhoneGap should be an easy way to access local files and resources.

Any tidbits of knowledge and feedback is as always greatly appreciated!

Michael
  • 1,742
  • 3
  • 18
  • 24

4 Answers4

1

As far as I can see, there are three ways to put content on the screen when using cordova/phonegap:

  1. Make an ajax call to a template file and then $(div).append
  2. Have it all included in index.html and show/hide
  3. Have the content inline in js files and $(content).appendTo(div)

I've been playing around seeing if any has advantages and here's what I've (in my limited tests) found out:

At first load, templates have the smallest memory footprint, followed by inline js and included in the index.html the highest.

But, after navigating around the app for a while, this changes and they all balance out to roughly the same (between 1.8 and 2.1 MB in my tests).

For speed, index.html is fastest, followed by inline js and templates is slowest. However, the difference is negligible, tens of milliseconds worth.

I honestly don't see any real difference between the methods, except for the fact that templates don't seem to work on Windows Phone (maybe that will change with 10, don't know). I guess it's more of a coding style thing rather than a hard and fast rule. Although input from cordova/phonegap devs would be nice.

hdezela
  • 536
  • 6
  • 16
0

I do not think the chrome security restrictions you are talking about aply for a webview or a cordova application (and they are not specific to chrome but apply to any modern browser).

I have tested using jquery ajax function to load resources (json files, javascript files or html templates) from files located inside the www folder in a phonegap application and it works in android 2.3, 4.0.4, 4.1.2 and also 4.4.2 on a nexus 5 (so using the new chrome webview).

QuickFix
  • 11,661
  • 2
  • 38
  • 50
  • Thanks, but you're wrong about those restrictions applying to any modern browser - Chrome is especially restrictive there by limiting XHR on HTML files loaded from local file:// filesystem (in my question above I've linked to a detailed discussion about that but there are also plenty discussions on that here on StackOverflow). Plus I've done extensive work on writing our own AJAX library so I can confirm that it does in fact work on other browsers. – Michael Feb 02 '14 at 22:34
  • You might however be right about there being something different about (Chrome-powered) webviews. I've installed the new Android SDK over the weekend and XHR from local files does seem to work on a 4.4 Android image simulator. Whether Chrome itself was adapted to do this when launched in a webview or its a feature of PhoneGap - I don't know. But I suspect it might be the latter. BTW can you confirm that this works on a real device in 4.4 (not just the simulator)? I mean your Nexus5 test wasn't simulated right? – Michael Feb 02 '14 at 22:38
  • I have seen loading local files failing in ie with default secutity options, as well as in firefox unless you require special privileges and in chrome unless you start it with option to disable security. You can have a look at the following link to see what I'm talking about : http://stackoverflow.com/questions/371875/local-file-access-with-javascript. Maybe you're using some techniques I'm not aware of. – QuickFix Feb 02 '14 at 23:03
  • 1
    Anyway, what I can confirm is that after I read your question, I took my girlfriend's brand new nexus 5, activated the developer options and tested my app that uses ajax to load json files and template files located inside the www folder. I noticed no issue on the nexus 5 using the latest updates. – QuickFix Feb 02 '14 at 23:07
  • Thanks for confirming that. As far as security settings on other browsers - nope, no special security/launch options. There are some browser-specific things involved in getting it to work across browsers, but in general requests don't fail - people just implement it wrong because it works differently local than over http (stuff like xhr header or status requests don't work on local files so some events fail too). If you for example look at the URL from my question you'll see Google engineers discussing if they'd implement it like in Firefox. But thats an old issue too... – Michael Feb 02 '14 at 23:16
0

Looks like you very confused with how Cordova works. Well made SPA application will just work with Cordova with very slight modifications related to security and Cordova boilerplate configs.

Loading static resources (images, html, css)

There no need to put these resources in some place, other then www location. With Cordova XHR requests work just fine if you use relative paths for making XHR requests. So having folder structure like that in your www folder

www
|
|- css
|   |- style.css
|- js
|   |- app.js
|- html
|   |- page.html
|
|- index.html

from your index.html you could do regular request using

$.get('page.html')

Also make sure that you set proper Content Security Policy (CSP) in your index.html. See below for example.

Dynamic resources

These resources which will change over application lifetime. And this is the resources for which you should use File API. This API does not require user interaction within Cordova application.

In the Cordova File plugin there documentation how each variable align with OS specific folders. See Where to Store Files section of docs.

Security

Latest changes in Cordova requires you to have CSP policy to be specified in your HTML, otherwise you will not be able to do any communications with external resources. You could see HTML5 tutorial on CSP for explanation, and Cordova Whitelist plugin docs for examples how to configure CSP in Cordova-specific way.

codevision
  • 5,165
  • 38
  • 50
0

There are several ways to easily structure and manage Cordova web applications now. I suggest XDK from Intel as the cost-free solution for building and structuring iOS, Android, and Windows phone apps: https://software.intel.com/en-us/intel-xdk

Chris Buck
  • 745
  • 5
  • 15