Skip to content
This repository has been archived by the owner on May 13, 2021. It is now read-only.

Support mobile-specific functionality #156

Closed
mattwoberts opened this issue Sep 11, 2015 · 33 comments
Closed

Support mobile-specific functionality #156

mattwoberts opened this issue Sep 11, 2015 · 33 comments
Milestone

Comments

@mattwoberts
Copy link

A common scenario would be to create a wrapper for a hosted web app, and then compliment that by adding some additional things like push notifications support etc). I'm unsure what the process is when you want to do this - you can add for example the plugin for push notifications, and then add the code to allow this to www/js/index.js, but this code won't fire, since the launch URL is the hosted web app URL itself...

From what I can see from reading around (http://code.tutsplus.com/tutorials/build-a-hosted-web-app-on-android-ios-and-windows-using-manifoldjs--cms-24444) it seems that the way to do this is to just add the cordova js files that are required directly to your web site, and then include them as script files in your html - am I on the right track here?

@boyofgreen
Copy link
Contributor

Hi Matt,
There is actually a different answer for each platform, and I'm working on some wiki posts about this to clarify it. We also have something in the works to try to automate this for the cordova polyfills:

  • FireFox OS: Accessable from page with JS. CHeck this resource to which ones are accesable from a hosted app, https://developer.mozilla.org/en-US/Apps/Reference/Firefox_OS_device_APIs
  • Chrome OS: limited to no device APIs, See this article for details: http://www.thishereweb.com/hosted-web-apps-explained/ In Chrome's defense, they also have the most Web APIs
  • Windows 10: Accessible from page with JS. API access can be turned on for any domain, my adding apiAccess="all" into your mjs_access_whitelist rules. Here is an example file: travelcontoso.azurewebsites.net/manifest.json
  • iOS / Android: this is a bit trickier, since it's a Cordova Polyfill, you need to add the cordova files to yoru website. These steps:
  1. move the content from the www folder (after adding plugin) to your webserver
  2. reference the cordova.js file and the cordova_plugins.js file from your page
  3. Load appropriate cordova.js content for each platform. This is where it gets tricky if you are supporting both iOS and Android (BTW, we build a windows 10 cordova platform as well, if you want to handle them all together). The cordova.js file, which is required for the pulgins to know where to load from (they use the path of this file). I'm playing with three different ways to solve this
    • do some client detection on the server to serve the right version, pretty easy in node.js
    • make the cordova.js file empty and then add the code back in a third js file called cordova_android.js and cordova_ios.js, for each polatform aproperately
    • remove the code from cordova.js and replace with a client detection that then loads the aproperate version.

As you can see, it takes a little bit of work, but once this is done, you can do all your updates from the web server, and never need to deploy again (until you change domain useage :) )

@mattwoberts
Copy link
Author

Thanks for that response Jeff.

I am indeed thinking about Ios / android mainly (as I suspect most developers will be too). Your steps make sense - and like you said once done it should be possible for all updates to come from the web server, which is a massive win :)

I think for me, client detection on the server seems like a nice solution, would certainly be the one that I'd look into. I'm using asp.net, so I don't think it's too much trouble for me to do some detection on the server which will then allow me to conditionally serve the right file - I like this also because it means I don't need to worry about serving cordova.js files for "normal" desktop browser clients that don't want it.

@boyofgreen
Copy link
Contributor

If your interested in colaboraing on this let me know. I bet it would be beneficial to the community. Maybe we can do an asp.net and node.js versions of it. I can probably find a person or two who knows asp.net :)

@mattwoberts
Copy link
Author

Time permitting, I'd like to try and help out if I can :)

On Tue, Sep 15, 2015 at 9:52 PM, Jeff Burtoft notifications@github.com
wrote:

If your interested in colaboraing on this let me know. I bet it would be
beneficial to the community. Maybe we can do an asp.net and node.js
versions of it. I can probably find a person or two who knows asp.net :)


Reply to this email directly or view it on GitHub
#156 (comment)
.

Matt Roberts

e: roberts.mattroberts@gmail.com
t: mattwoberts

@vjrantal
Copy link

iOS / Android: this is a bit trickier, since it's a Cordova Polyfill, you need to add the cordova files to yoru website.

I was thinking about this a bit and was wondering if the setup ends up getting complicated when time goes by. You may update Cordova to a newer version in the app package you have in stores. That would mean that you would have to host different Cordova (and plugin) JavaScript to different platforms, but also for all the app package versions you have deployed via stores (because not all of the end users update at the same time).

From this perspective, a better approach would be to load Cordova from the local folder where there already is the correct version of the JavaScript files. As a proof-of-concept of this model, I implemented an example at https://github.com/vjrantal/manifoldjs-cordova-example that conditionally loads Cordova and uses functionality from a plugin.

The biggest limitation I see at the moment is on iOS 9.0 the case where you need to serve your site over a secured connection. I am still trying to find a workaround, but no luck so far. The case works on iOS version < 9.0, but that doesn't help since an app must work with the latest. As a side note, I think this same limitation would be there even if you host the Cordova files in your Web server.

@boyofgreen Let me know how you feel about the approach described above. If you agree that it is a good model, I'll try to get a required commit upstreamed and some further documentation (as a blog post) to make it easier for someone to adapt the approach.

@boyofgreen
Copy link
Contributor

We actually talked about this tool. The idea we had was to do them as js injection into the page. I'll email you the POC one of the developers started working on. If we did injection (copy js code, concat, inject) in place of your flie load:
'cdvfile://localhost/assets/www/cordova.js'
wouldn't that get around the security breakage on iOS?

@vjrantal
Copy link

We actually talked about this tool. The idea we had was to do them as js injection into the page. I'll email you the POC one of the developers started working on. If we did injection (copy js code, concat, inject) in place of your flie load:
'cdvfile://localhost/assets/www/cordova.js'
wouldn't that get around the security breakage on iOS?

I didn't actually expect it would, but based on the my testing, this would get around the limitation.

The limitation was that on iOS 9.0, a script-tag with source pointing to cdvfile-scheme was denied when the hosting page origin was secured. However, seems that iframe-tag pointing to a similar custom scheme is allowed which means that the bridge Cordova uses on iOS works.

@vjrantal
Copy link

The biggest limitation I see at the moment is on iOS 9.0 the case where you need to serve your site over a secured connection. I am still trying to find a workaround, but no luck so far.

A fairly decent workaround (that at least buys some time) is to use an older SDK.

@mattwoberts
Copy link
Author

@boyofgreen Right - I'm farily new to this - so what you're saying is that you check on the client what version of cordova is running, and based off that you then inject the required scripts, pretty much like @vjrantal has done, but sourcing the extra scripts from the server, right?

@mattwoberts
Copy link
Author

I'm playing with this now.. in a simple test I'm serving the cordova files from the server, based on inspecting the user agent strings.

An issue I have with this solution is that I want to send the cordova files ONLY if the web pages are being served via the cordova app - I don't want to do this if they're requested from the mobile browser.

I think to solve that, I need to set a custom user agent string for the webview in cordova, which itself is tricky, because I'm working with a CordovaWebView which doesn't support setting the user agent string (http://stackoverflow.com/questions/30323529/modify-cordovawebview-user-agent-string-in-android-4-4-cordova-4-3-gwt)

I'll keep digging away at this ..

@yasin-siddiqui
Copy link

@mattwoberts : Instead of looking for the custom user agent, I think it might be better in a live environment to point another domain/subdomain to the same www/public_html folder on your web-server, and then check for the domain name in the host [window.location.hostname in JavaScript] / [$_SERVER['HTTP_HOST']) in PHP]. So www.domain.com can render the site without cordova js and app.domain.com can render the site with the cordova js injected dynamically.

@mattwoberts
Copy link
Author

@yasin-siddiqui Hmm, that sounds reasonable - it would also allow me to do the "am i running in the mobile app" check both on the server and on the client easily enough.. Assuming there's not too many hard coded links in the app to worry about, I'll try that...

@EdSF
Copy link

EdSF commented Oct 25, 2015

+1 The use case above is extremely interesting because IMHO it is the "normal" progression if using this approach (making use of existing investments in a web site/application).

My n00b tinkering with the approach so far has me hitting this behavior described on SO which I believe underscores the importance of detecting and loading (only) for the right platform (including desktop, mobile browser).

The behavior is also reproducible by loading the generated android/assets/www/index.html in Chrome.

@vjrantal - your post/sample is illuminating - thank you.

@yasin-siddiqui - being the noob, I'm likely very wrong here, but IINM, manifoldjs allows us to trivially package an existing web site, so, if we're going to "manage" a "subdomain" (or similar approach), in essence a "different" app, then wouldn't it be "better" to invest in writing a "formal" mobile web app (in the first place)?

@mattwoberts
Copy link
Author

@EdSF Ya, if you try to load the cordova files and you're not running in the webview (in the packaged app) then you get all sorts of horrible errors like that.

That's why we need to only load the cordova.js files when we're being run inside the packaged app.

The suggestion made by @yasin-siddiqui is a simple "hack" - have a different subdomain that just points to the regular app. That way, in the server-side code, you can check the host - if it's "mobile.myapp.com" then you write the script tags for the cordova.js files, otherwise you skip 'em.

It's a good suggestion, but not one that's going to work for me (SSL issues, I'd need to buy additional SANs), so I'm mulling over alternatives.... I either go back to the user agent, or I do something like pass a querystring in the webview URL (something like www.myapp.com?mobile=true) - and then set something on the session when that's set - but then sessions expire so that's not good, plus sessions are generally evil.

@boyofgreen
Copy link
Contributor

We're working on a few ideas here as well. We think we can make some decisions on the client to inject the proper version of the cordova.js file so that the hard part of determining the os is done for you. we just need to get enough time to do some research and testing.

@mattwoberts
Copy link
Author

@boyofgreen Any idea on timeframes for that - also, is there anything I can do to help?

@mattwoberts
Copy link
Author

@boyofgreen Could you give a TL;DR for how you're approaching this - I need to do something similar pretty soon so a brief description of how you're going about this would be awesome :)

@f2bo
Copy link
Contributor

f2bo commented Nov 18, 2015

We've already made some progress that will allow the use of Cordova plugins in a hosted web app. We now support injecting Cordova and the plugin interface scripts into the pages of the hosted site. We're still working on this feature and it's not published yet, but if you're interested, you can start playing with it right now if you're willing to follow a few manual steps.

You can find detailed information about the new plugin support in this wiki article.

We implemented two different plugin modes: server and client. In 'client' mode, the cordova.js file and the plugin interface script files are retrieved from the app package. In 'server' mode, these files are downloaded from the server along with the rest of the app's content.

We also implemented a mechanism for injecting scripts that can be used, among other things, to consume the plugins added to the app. Imported scripts can be retrieved from the app package or downloaded from a remote source.

Until ManifoldJS is updated to use the new features, you will need to manually replace the hosted web app plugin in your app. The wiki article provides a Quick Start that explains the process.

Very briefly, these are the steps that are needed to use plugins:

  • Add one or more Cordova plugins to the app.

  • Update the app's manifest to enable API access and to inject the Cordova runtime environment. The match attribute specifies the pages where you will use Cordova.

    {
      ...
      "mjs_api_access": [
        { "match": "http://yoursite.com/path1/*", "platform": "android;ios;windows", "access": "cordova" },
        ...
      ]
    }
    
  • Optionally, choose a plugin mode. The default mode is client.

    Client mode

    {
    ...
      "mjs_cordova": {
        "plugin_mode": "client"
      }
    }
    

    Server mode

    {
      ...
      "mjs_cordova": {
        "plugin_mode": "server",
        "base_url": "js/cordova"
      }
    }
    

    (In 'server' mode, the Cordova files and plugin interface scripts must be deployed to the site to the path specified in base_url. Also, the cordova.js and cordova_plugins.js files for each platform need to be renamed to specify the platform in their names so that cordova.js and cordova_plugins.js become, in the case of Android for example, cordova-android.js and cordova_plugins-android.js respectively.)

To inject scripts into the hosted web content:

  • Update the app's manifest to list the imported scripts.

    {
      ...
      "mjs_import_scripts": [
        { "src": "js/alerts.js" },
        { "src": "http://yoursite.com/js/app/contacts.js" },
        { "src": "js/camera.js", "match": "http://yoursite.com/profile/*" },
        ...
      ]
    }
    
  • For app-hosted scripts, copy the script files to the Cordova project. The path in mjs_import_scripts must be specified relative to the 'www' folder of the project. Server-hosted scripts must be deployed to the site.

@mattwoberts
Copy link
Author

@f2bo This sounds brilliant, thanks - you saved me from a world of pain trying to sniff UA strings ;) I actually met @boyofgreen yesterday in Manchester, UK, and he told me you were working on this :) I'll be sure to check it out soon

@EdSF
Copy link

EdSF commented Nov 19, 2015

👍 I must play with this in the next few days @f2bo - thank you!

@kenchris
Copy link

I find the "server" vs "client" a bit confusing... why not just call it "locally" "remotely" or local/remote

Or give it a baseurl which could be a msapp-x: url (or what it is called) for local plugins

@ghost
Copy link

ghost commented Nov 25, 2015

Agree that "server" and "client" might cause some confusion, but wondering if "locally" and "remote" will cause a similar effect.

Perhaps the confusion is not in the values of the setting, but rather in its name (plugin_mode). Maybe renaming it to plugin_source, and using "package" and “web" for the values would make more sense.

In any case, this setting is only required to override the default value, which is "client". We believe that most developers will stick with "client" mode and this setting will not be used frequently.

@kenchris
Copy link

"self-distributed": false?

@kenchris
Copy link

I don't like "source", as "source" or "src" normally expects a url as value.

@kenchris
Copy link

So, you fetch the cordova plugin before packaging the app, or are they downloaded when running the app first time, or how does it work?

@f2bo
Copy link
Contributor

f2bo commented Nov 25, 2015

"So, you fetch the cordova plugin before packaging the app, or are they downloaded when running the app first time, or how does it work?"

Plugins are installed at design time using the regular Cordova mechanism (i.e. cordova plugin add cordova-plugin-xxxx). This embeds the plugin's native code and its Javascript interface files in the app's package. So far, this is standard Cordova.

At runtime, a stub loader is injected into the hosted web pages where API access has been enabled. Depending on the configured plugin mode, the loader injects cordova.js into pages by retrieving it from the app's package (client mode) or by downloading it from the site (server mode). Once Cordova is loaded into a page, it also loads any plugins that were added to the app. The stub loader also intercepts the loading of these plugin interface files and handles them in the same way as cordova.js, retrieving them from the app package or the site depending on the mode.

There's additional information in the wiki.

@kenchris
Copy link

Optionally, set the Cordova plugin mode to choose between 'client' or 'server' modes. In 'client' mode, the cordova.js file and the plugin interface script files are retrieved from the app package. In 'server' mode, these files are downloaded from the server along with the rest of the app's content. The default is 'client' mode.

Retrieved from which server? The Cordova repo? At least to me it sounds like something people would rarely change and my suggestion would then be to turn it into a boolean instead. like

"fetch_scripts": true

or "fetch_scripts_from_server"

Then even if people also distribute the scripts in their package, it will always go out and refetch them from the repo (makes it easy to do experimentation"

@f2bo
Copy link
Contributor

f2bo commented Nov 26, 2015

..."Retrieved from which server?"...

That would be whatever server you specify in the baseUrl setting.

Typically, this setting will contain a relative path pointing to the location of the Cordova files in the hosted site, though it can also be a full URL pointing to a different origin. Even if you assume that the Cordova files are always stored in the hosted site, in 'server' mode you still need the baseUrl setting to specify the path to these files.

@ghost ghost added this to the release-0.4.3 milestone Nov 26, 2015
@mattwoberts
Copy link
Author

This is working well for me - thanks for the efforts 👍

IMHO, I'm also happy with the server/client distinction in the manifest

@kenchris
Copy link

Then why not let it be "client" if no (or a local url) is defined as the baseUrl? I mean, you can infer it from that.

@f2bo
Copy link
Contributor

f2bo commented Nov 30, 2015

That´s how it works. If you don't specify a plugin_mode, it defaults to client. The base_url is not used in this mode (you don't need to specify a local URL), so you can skip the entire mjs_cordova section for client mode.

@kenchris
Copy link

Then why do you need plugin_mode at all? :-) if base_url is set, you can easily infer whether the plugin_mode should be server.

@boyofgreen
Copy link
Contributor

closing old issues, looks like it was solved for those who needed itt

@ghost ghost locked as resolved and limited conversation to collaborators Feb 4, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants