I had two major hurdles to deal with:
First, the Cordova plugin code needs to run in the "browser" context, not in the Cordova app's context. So, the Cordova plugin JavaScript code that sets up the calls into the native code on the device to interface with the camera, device info, or what have you needs to be fetched as an Asset from the Website.
Second, I needed to be able to differentiate an access by one of my Apps running on a device from a normal web browser access (potentially running on that exact same device).
It should be noted that my Cordova app uses a number of plugins. A few are device platform specific (e.g. iOS only, or Android only), which means my site needs to serve up a different set of assets depending on the device making the request.
Before we go much further let's talk versions... I used Cordova 3.5.0, Rails 3.2, and hosted my site on Heroku. Git was used for the SCM. In addition, I have one file that I've pulled from Cordova that's NOT STANDARD. It allows a Cordova app to use minified/compiled assets. See Cordova Issue CB-6505. I pulled copies of cordova.js for each platform I'm using from this Cordova JS Git Repo. There are plans to build a permanent fix for this, but this code is not, to my knowledge, in the main Cordova release yet. This is the only non-standard base code in my project.
I have 2 Git repos that I use. One holds my Rails-based website code and I'll call it 'web-site-repo' here. The other holds my Cordova repo, which I'll call 'app-repo'. There is some transferring of files from the app-repo to the web-site-repo (described below). I also maintain 2 branches, besides the master branch, on each repo. The master branches refer to the Rails "development" environment, which resides on my local development machine. The other two branches refer to my Rails "staging" and "production" environments/configurations.
For the most part the app-repo branches differ only in what site gets initially loaded in the top-level config.xml and which sites are white-listed. In the master, I use the current IP address of my development machine for the initial load. In the "staging" and "production" branches, I point instead to my staging and production Heroku instances. The "staging" branch uses just the name assigned by Heroku under ".herokuapp.com". "Production" points at my own domain name, which refers to my Heroku production site. And the whitelist entries vary some across the 3 branches. This makes merges from the "master" into either the "staging" or "production" branches simple and efficient.
Another key change in the app-repo is that I've added files into the Merges subdirectory to help all of this along. In my 'merges' directory (app-repo/merges) I have subdirectories for each platform I support, and in there are the related 'cordova.js' files from the above cordova-js.git repo. I even have a few files under the Hooks directory to move the appropriate cordova.js files around, as well as handle things like Icons and Splashscreen files that don't seem to always make it to where they should, if I just let Cordova do it's thing.
As I said before, I also need to be able to tell when my site is getting a request from a device running one of my Cordova apps vs. a standard browser. To do that, I've modified the User Agent string in the requests. And this chunk of modified code is also kept in the Merges directories for each platform. For Android the change I made was in CordovaWebView.java. I have the following in the CordovaWebView setup() method:
WebSettings settings = this.getSettings();
settings.setUserAgentString("MY-APP-ANDROID");
static NSString* const MYUSERAGENT = @"WIMW-APP-IOS"; /* near the top of the file */
gOriginalUserAgent = [NSString stringWithFormat:@"%@ %@", MYUSERAGENT, systemAndLocale];
It's pretty much the same thing on the Rails side vis-a-vis the 3 branches I use, except for how assets are handled. In Rails 3.2, assets in the "development" configuration are not pre-compiled. They're just fetched each time an individual file is needed - speeding up the development process. However, in the "staging" and "production" configurations, assets are pre-compile and minified - essentially stripping out white space, comments, etc. and concatenating the asset files together into a single file. The precompilation/minification speeds up both getting the assets in the first place (one website fetch for each type - JavaScript, CSS, images) and removes non-code so that parsers don't waste runtime doing it every load.
In my Rails web-site-repo I have my JavaScript assets directory (web-site-repo/app/assets/javascripts) arranged like this:
application.js iOS.js android.js app.js iOS/cordova.js iOS/cordova_plugins.js iOS/plugins iOS/plugins/org.apache.cordova.dialogs/www/notification.js
and similarly for the android subdirectory. There are more plugins of course...
I had to modify my application.js file to include only the JavaScript files that are universal to all the platforms I support. In my case, it looks like this:
//= require jquery //= require jquery_ujs //= require bootstrap //= require app
The iOS.js and android.js files are also Manifest files, which pull in their related Cordova plugin code. The iOS.js file has this:
//= require_tree ./iOS
The iOS/cordova.js and android/cordova.js are the files mentioned above. The cordova_plugins.js files are built by Cordova when I run 'build cordova' on my app-repo. After Cordova finishes building, I copy the cordova_plugins.js files from the app-repo/platforms directories - for example:
cp app-repo/platforms/ios/www/cordova_plugins.js \
web-site-repo/app/assets/javascripts/ios
cp app-repo/platforms/android/assets/www/cordova_plugins.js \
web-site-repo/app/assets/javascripts/android
I also copy all the JavaScript code for each of the plugins, again from each of the Cordova platform
directories I support.
cp -r app-repo/platforms/ios/www/plugins/* web-site-repo/app/assets/javascripts/ios/plugins
cp -r app-repo/platforms/android/assets/www/plugins/* \
web-site-repo/app/assets/javascripts/android/plugins
You only need to do the copying of all this if something changes - typically when you add/remove a plugin.
And, I added a "before filter" on the Rails side that looks at "request.user_agent" and decides if the current access is coming from a web browser, or the Cordova app running on one of my supported platforms. The "before filter" sets a few @variables that later get used to decide, for example, how a button should look. Some of these @variables are used to decide what JavaScript to load in the Rails app's app/views/layout/application.html.erb file. This is where the code loads only the correct set of assets using the @variable set by the before filter. For example:
<% if @Req_from_iOS_app %> <%= javascript_include_tag "iOS" %> <% elsif @Req_from_android_app %> <%= javascript_include_tag "android" %> <% end %>
near the end of the application.html.erb file
At present, this all works pretty well. It's a bit slow. The next step will be to convert to some form of Singe Page App so that every page load doesn't have to reload all the plugin code.
It should also be noted that, while I was moving the JavaScript files around, a Safari update caused me to see new errors on Safari on a desktop. The popup was stating: "There is no application set to open the URL gap://ready". See Cordova Issue CB-7072 for more detail. Ensuring that none of the cordova.js files got loaded except when the request was from my Cordova App fixed this.
Is there an example or a GIT repo which I can take a look at, not sure if I can make it only based on comments and explanations.
ReplyDeleteI don't have an example. Which part's giving you trouble?
ReplyDeleteIts hard to believe there is no way to use the assets that are local to the platform.
ReplyDeleteAnyway, I put cordova.js, cordova_plugins.js and the entire plugins folder in my rails app (I have separate folders for android and ios files). My rails template has logic to load the cordova.js from the server. It works fine in android. I can init the app and on device_ready I can call the native notification via navigator.notification.alert() and I see the alert.
However, in ios the onDeviceReady function is never called. Via the Safari debugger I can see that document.addEventListener('deviceready', this.onDeviceReady, false); is indeed begin called. There are no errors in the console either.
Did you have any issues like this? I'm using Cordova 6.2.0
I got it working. I needed to install the cordova-plugin-file plugin. I'm not sure why its necessary though. The reason it was working for android was because I had installed that plugin in my android env while messing around with something else.
DeleteI've been able to get this working with all my Cordova assets in the Rails public folder. However, when I tried to put these in the asset pipeline I can see how this would possibly work (in my dev environment at the moment). Perhaps the latest cordova javascripts are just not compatible with asset pipeline? For one thing there is a method on cordova.js for injecting the cordova_plugin.js. This fails because findCordovaPath doesn't find where cordova.js is.
ReplyDeleteEven if that injection worked I can't see how cordova_plugins.js would work properly. It contains paths to files like:
"file": "plugins/cordova-plugin-network-information/www/network.js"
Both the path and the file name will be incorrect after asset compilation. When the network.js asset is compiled it will be located at something like:
assets/cordova/android/plugins/cordova-plugin-network-information/www/network.self-e81ae1910fcbb2442f98cd6b6e17221fc29384d2bb3de630be37bd3d9b1922ba.js
Turns out there is a switch in Cordova that allows you to compile all the plug-in (etc) js into a single cordova.js file. See --browserify. Works great for creating an asset pipeline compatible file
DeleteGreat post.
ReplyDeletehttps://www.behance.net/virohaa1