Open gliechtenstein opened 7 years ago
@gliechtenstein I'm happy to work on this as I've just done the work on #162 so am in the webview head space atm.
@maks sounds great! 👍👍
This will be implemented per the doc issue: https://github.com/Jasonette/documentation/issues/66
@gliechtenstein I've got an initial working implementation: https://github.com/maks/JASONETTE-Android/commit/5ed6938c822507d8c391faecf1e6806a9f1563f5
BUT I haven't done the PR yet due to the interface for objects added to a Webview in Android via addJavaInterface()
only allowing simple types we need to pass the Jason from Webview JS to Java via JSON string (JSON.stringify()
) not an actual JS object. Would you be able to do it this way on iOS to be consistent?
@maks I was thinking about that issue too. I guess we could do that in the worst case, but it feels a bit too inconvenient because then the user would have to write the code in escaped JSON string everytime and it's really cumbersome and not intuitive.
Would it be possible to inject an additional javascript on the Android side so that it automatically interprets a JS object into a JSON string before evaluating?
@gliechtenstein I guess we can load a bit of JS shim code to do this as long as we DONT target N
where this will no longer work, [per this compatibility note](https://developer.android.com/reference/android/webkit/WebView.html#evaluateJavascript(java.lang.String, android.webkit.ValueCallback
The problem with doing the above is we permentantly stop Jasonette from ever targeting Android N
or higher.
An alternative to that is to require webcontainer content that wants to use JASON
to do so via loading a JS library (eg. via CDN or locally) which could then hide this and any other future compatibility issues.
@gliechtenstein I saw you merged the iOS implementation for this. Are you ok then for me todo PR with implementation I have for Andriod with the caveat of needing to stringify()
the return value?
Oops sorry i totally dropped the ball on this one, shouldn't have merged that iOS one before this issue was resolved in this world. Thankfully an API doesn't exist until it's documented so I think we have some room for now.
An alternative to that is to require webcontainer content that wants to use JASON to do so via loading a JS library (eg. via CDN or locally) which could then hide this and any other future compatibility issues.
Could you elaborate a bit on this? Do you mean like injecting some JavaScript into the webview? And how is this different from the JS shim approach? I guess I didn't fully understand the JS shim approach to begin with...
@gliechtenstein no worries.
What I meant would be to have content in a webview include a js file:
<script src="https://jasonette.org/webview-shim.js"></script>
and then webview-shim.js would expose whatever API we are going to document and the JS code in that would take care of doing anything needed to make it compatible across iOS and ANdroid, eg. needing to stringify()
on Android.
Hope that makes sense?
@maks I think this is kind of related to https://github.com/Jasonette/Jasonette/issues/4 we're discussing. I guess now that we're going to be injecting JS files into JS execution context I guess there's not much holding us back from doing it this way?
@gliechtenstein no sorry its different, there we are talking about a webview whos content jasonette "controls" whereas here its any html content supplied be the user.
@maks OK I've just realized my confusion stems from the fact that the anchor tag part of the link you pasted is broken and simply opens up at the top of the https://developer.android.com/reference/android/webkit/WebView.html page. I didn't understand because I didn't realize you were talking about the valueCallback part specifically.
Anyway, now that I understand I can make a more constructive comment hopefully.
Basically the way I've implemented this logic on iOS side is
JASON.call
and inject it to the webview contextInstead of having JASON.call
run some JS-to-native binding logic, the JASON.call
simply creates an <iframe>
element with its src
attribute set to the JSON object parameter we pass in to JASON.call
https://github.com/Jasonette/JASONETTE-iOS/pull/253/files#diff-f22c800ecb5b9ecf974c06bdcb66ad7cR1881
NSString *summon = @"var JASON={call: function(e){var n=document.createElement(\"IFRAME\");n.setAttribute(\"src\",\"jason:\"+JSON.stringify(e)),document.documentElement.appendChild(n),n.parentNode.removeChild(n),n=null}};";
[webView stringByEvaluatingJavaScriptFromString:summon];
This JS creates an iframe that looks like this:
<iframe src="jason://{\"trigger\": \"some_action\"}">
(Note that we also make sure the iframe is cleaned up after this function is triggered. If you look at the script it calls removeChild
to get rid of the created iframe right after execution)
jason://
, we extract out the JSON and parse it into dictionary and use it: https://github.com/Jasonette/JASONETTE-iOS/pull/253/files#diff-f22c800ecb5b9ecf974c06bdcb66ad7cR1886Basically iOS has the same challenge as the challenge you brought up for higher version Androids. There is no way to directly make native calls from webview, and this is how we achieve it.
And since this problem is not unique to Android, I was thinking maybe we could do the same thing for Android.
shouldOverrideUrlLoading
and watch out for the jason://
url, and when we come across one, parse it into JSONObject
and use it.How does that sound?
@gliechtenstein sorry about the broken link. I think we may have our wires crossed here...
On Android you can just "native calls" into Java directly. You use addJavascriptInterace() to inject a Java object into the global JS scope of the webview, then html content can call public methods of that object, there is no need at all for the iframe dance you need to use on iOS.
BUT there are constraints on the above, one of them is that the public Java methods you expose can only have "primitives" as parameter types, so you can pass in only numbers, strings, booleans but not JS objects. Hence the need to JSON.stringify().
So on Android its very straightforward to inject a Java object named JASON
into the webviews global JS scope, its just not possible to pass it a object as a parameter, but a json string is fine.
To be honest to my mind this seems like a very small requirement to place on anyone making use of this interface, its just it would be nice for it to be consistent across iOS and Android.
Will there are any solution to do calls from JS to JASON?
Should work the same way it works for the iOS branch
https://github.com/Jasonette/JASONETTE-iOS/pull/253