Can iframes work in WKWebView with Cordova? - ios

According to the Apache Cordova blog, iframes may not work using WKWebView. (https://cordova.apache.org/news/2018/08/01/future-cordova-ios-webview.html)
I have a Cordova application that is in the App Store that relies quite heavily on iframes. Since UIWebView is likely to be removed in iOS 13, is there a way to get iframes working using WKWebView?
Here's what I've done so far:
I tried using the Ionic WebView plugin (https://github.com/ionic-team/cordova-plugin-ionic-webview), and although it works for parts of my app it does not work on the iframe pages. Specifically, I'm getting Access-Control-Allow-Origin header contains the invalid value 'null'. I don't get this error using UIWebView.

Add this in your config.xml
<allow-navigation href="*" />
Then build your ios platform

I ran into this issue also in my Cordova apps, and have found a workaround. It involves writing content directly to the iframe, rather than giving it a src="...". This way, iframe runs as same-origin as parent.
(Iframes do work in WkWebView; it's just that anything loaded via src="file://..." [e.g. all local app files] are treated as unique-origin, so tends to screw up any cross-frame JavaScript.)
function frameEl_injectHtml(frameEl, injectHtml) {
// frameEl must exist in DOM before its contentWindow is accessible.
if (!frameEl.contentWindow) { alert("frameInjectHtml() but frameEl not (yet or anymore) in DOM."); return; }
frameEl.contentWindow.document.open('text/htmlreplace');
frameEl.contentWindow.document.write(injectHtml);
frameEl.contentWindow.document.close();
}
// Create <frame>, insert into DOM, and inject content.
var frameHtml = "<html><head></head>" +
"<body>" +
"iframe body" +
"<script>window.parent.alert('iframe even same-origin as parent.');</script>" +
"</body></html>";
var frameEl = document.createElement('iframe');
frameEl.src = "about:blank";
document.body.appendChild(frameEl);
frameEl_injectHtml(frameEl, frameHtml);

Add this to your Cordova config.xml
<allow-navigation href="http://*.yourdomain.com/*" />
It will allow your HTML pages, no matter root documents or children in the iframe, to redirect from localhost to a remote URL.

Related

In google sheets add some kind of dialog box that will display on an ipad

I am trying to add a yes/no message box to a google sheet that will display on an iPad. I have tried all the things below but none of them display the message. My goal is if the user changes a field the macro will change other fields if the user says it is ok. I can get this to work on a windows machine with no issues but when I try it on an ipad the message never appears. At this point I am just trying to get a message box to appear. This is what I have tried
1.
function onEdit(e) {
Browser.msgBox("test");
}
Result: The macros times out
2.
function onEdit(e) {
var html = HtmlService.createHtmlOutputFromFile('Page')
.setWidth(400)
.setHeight(300);
SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
.showModalDialog(html, 'My custom dialog');
}
Page.html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
Hello, world! <input type="button" value="Close" onclick="google.script.host.close()" />
</body>
</html>
I enabled the trigger for onEdit to get it to work and it works on the windows machine without issues but when run on the iPad I get:
Exception: You do not have permission to call Ui.showModalDialog. Required permissions: https://www.googleapis.com/auth/script.container.ui
So next I tried:
3.
function myFunction(){
Logger.log("myFun")
var html = HtmlService.createTemplateFromFile( 'Page' )
.evaluate()
.setWidth( 800 )
.setHeight( 400 );
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.showModalDialog( html, 'My page title' );
}
function onEdit(){
ScriptApp.newTrigger('myFunction')
.forSpreadsheet(SpreadsheetApp.getActive())
.onEdit()
.create();
}
Using the same page.html, this time in executions it looks like it runs but no box appears on the ipad(it does work on windows). I also tried spreadsheetApp.getUi().alert and prompt, these had the same results.
And Yes I have to use the iPad, I can not use another type of tablet.
Issue:
"Exception: You do not have permission to call Ui.showModalDialog. Required permissions: googleapis.com/auth/script.container.ui"
Fix:
What you can do is installing the trigger instead of using a simple trigger. The image below shows the difference between an installed (above) and a simple (below) trigger using the same code.
Output:
Note:
Don't use reserved function names on installed triggers to avoid it being executed twice like what's shown above. Change onEdit to something like promptUI or something else.
If the above fix isn't enough to show the dialog box, then we need to confirm if the issue might just be on the safari browser (if you are using one) since it worked on your other devices which I assumed are not using safari. You can try using an alternative browser in your device such as Google Chrome and see if the same issue persists. I recommended doing this since i'm seeing a number of issues between showModalDialog and safari
EDIT:
I have seen these related posts pointing out that there are some limitations on mobile apps. See the references below.
Also, some answers suggests that you need to access the spreadsheet via browser (google chrome desktop mode) and then trigger the script there.
References:
Add a script trigger to Google Sheet that will work in Android mobile app
google speadsheet api onOpen does not work on mobile ios
Executing Google Apps Script Functions from Mobile App
How to get scripts to work with phones and tablets

NativeScript Webview newbie questions

I am experimenting with using NativeScript to speed up the process of porting an existing Android app to iOS. The app in question uses a great deal of SVG manipulation in a Cordova webview. To keep things simple I want to port all of my existing Webview side code - in essence the entire existing Cordova www folder and its contents - over to the new NativeScript app. The WebView talks to a custom Cordova plugin which I use to talk with my servers to do such things as get new SVGs to show, keep track of user actions etc.
If I an get through these teething issues I am considering using this component to implement bi-direction communications between by current webview JS code and the, new, NativeScript backend that will replace my current Cordova plugin. Someone here is bound to tell me that I don't need to do that... . However, doing so would mean throwing out the baby with the bathwater and rewriting all of my current Webview ES6/JS/CSS code.
This is pretty much Day 1 for me with NativeScript and I have run into a few issues.
I find that I cannot get rid of the ActionBar even though I have followed the instructions here and set the action bar to hidden.
I can use the following markup in home.component.html
to show external web content. However, what I want to really do is to show the local HTML file that is in the www folder in the following folder hierarchy
app
|
____home
|
____www
|
______ index.html
|
______css
|
______ tpl
|
.....
However, when I use the markup
<Page actionBarHidden="true" >
<WebView src="~/www/index.html"></WebView>
</Page>
I am shown the error message
The webpage at file:///data/data/com.example.myapp/files/app/www/index.html is not available.
I'd be most grateful to anyone who might be able to tell me what I am doing wrong here - and also, how I can get rid of that action bar which is currently showing the app title.
About using local HTML file
Is your local HTML file recognized by Webpack (which is enabled by default in NativeScript)? Try to explicitly add the local HTML file to your webpack.config.js file. This way Webpack will "know" that it will have to bundle this file as well.
new CopyWebpackPlugin([
{ from: { glob: "<path-to-your-custom-file-here>/index.html" } }, // HERE
{ from: { glob: "fonts/**" } },
{ from: { glob: "**/*.jpg" } },
{ from: { glob: "**/*.png" } },
]
Example here
About hiding the ActionBar
NativeScript Core only: Try hiding the action bar directly for the frame that holds the page. See the related documentation here
NativeScript Angular: The page-router-outlet will have an action bar by default (you can hide it by using the Page DI as done here). Otherwise, you could create a router-outlet (instead of page-router-outlet). The router-outler won't have the mobile-specific ActionBar.

Cordova - Displaying local image files in a <img> tag

I have a Cordova app (built on Ember.js, using the Corber addon) that I am currently only running on iOS, specifically the iOS simulator (iPhone 8, iOS v12), so it is using a WKWEBVIEW.
The app is using cordova's file plugins to download remote images and store them on the user's device, currently in the "Documents" (cordova.file.documentsDirectory), which works fine, and I've verified that the images do in fact exist there. However I can't figure out the correct URL/URI to use to display those images in the app via standard <img> tags.
Cordova's own documentation says to use the cdvfile:// protocol path (https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-file/#cdvfile-protocol), but even with following the instructions there (adding the content-security meta tag to the index.html, the access rule to the config.xml, etc), it seems like WKWEBVIEW just flat out does not support this protocol, giving me "unsupported URL" errors in the console when I attempt to use it.
Other SO answers on the subject suggest using a normal URL path relative to the cordova app's web server URL, e.g. things like <img src="/Library/NoCloud/path-to-my/file.jpg"> but any path I try 404's.
This seems like such a simple/common use case, but I'm stumped.
For anyone else struggling as I was - there is also a solution, which requires no significant change to the code which I found after hopeless days when no solution seemed available
There are 2 steps required:
First update your config.xml with following
<platform name="ios">
<preference name="scheme" value="app" />
<preference name="hostname" value="localhost" />
</platform>
Then convert your file:// link by using the undocumented method
window.WkWebView.convertFilePath(filePath)
This method performs the conversion into a virtual localhost link that makes the file accessible and bypasses the WkWebView restrictions. A little bit longer sample goes like this
let localFile = cordova.file.dataDirectory + 'logo.png';
let convertedPath = window.WkWebView.convertFilePath(localFile);
document.getElementById("myImg").src = convertedPath;
I did not work with Cordova but docs you linked say this:
To use cdvfile as a tag' src you can convert it to native path via toURL() method of the resolved fileEntry, which you can get via resolveLocalFileSystemURL - see examples below.
And gives this as an example:
resolveLocalFileSystemURL('cdvfile://localhost/temporary/path/to/file.mp4', function(entry) {
var nativePath = entry.toURL();
console.log('Native URI: ' + nativePath);
document.getElementById('video').src = nativePath;
Given all that, I would say that you can try to create a component, cdv-img. Something like this supposed to work, I think:
import Component from '#ember/component';
export default Component.extend({
tagName: 'img',
cdvPath: undefined,
didReceiveAttrs() {
this._super(...arguments);
if (this.cdvPath) {
resolveLocalFileSystemURL(this.cdvPath, (entry) => {
this.$().attr('src', entry.toURL());
});
}
},
});
Use it like this:
{{cdv-img cdvPath='cdvfile://localhost/temporary/path/to/file.jpg'}}
UPD
If it does not work with file protocol, you can try to convert image to data url
import Component from '#ember/component';
export default Component.extend({
tagName: 'img',
cdvPath: undefined,
didReceiveAttrs() {
this._super(...arguments);
if (this.cdvPath) {
const el = this.$();
resolveLocalFileSystemURL(this.cdvPath, (entry) => {
entry.file(function (file) {
const reader = new FileReader();
reader.onloadend = function() {
el.attr('src', this.result);
};
reader.readAsDataURL(file);
});
});
}
},
});
Make sure you don't forget to add these 2 lines and try with whatever solution you are trying, I got my images shown after adding them:
In your config.xml file. add:
<preference name="allowFileAccessFromFileURLs" value="true" />
<preference name="allowUniversalAccessFromFileURLs" value="true" />

Use local json file with Cordova/ionic/Angular. Works in browser, but not on device?

I'm attempting to use a JSON object living in a data.json file to be the dataset for a quick prototype I'm working on. This lives in a my_project/www/data/ directory. I have an Angular service that goes and grabs the data within this file using $http, does some stuff to it, and then it's used throughout my app.
I'm using Cordova and Ionic. When using ionic serve on my computer, everything looks perfect in the browser. However, when using ionic view (http://view.ionic.io/) and opening the app on my iPad, I see a:
{"data":null,"status":0,"config":{"method":"GET","transformRequest":[null],"transformResponse":[null],"url":"../data/items.json","headers":{"Accept":"application/json,test/plain,*/*}},"statusText":""}
for a response. I would think that if it were a relative URL issue, that it would also not work in the browser, but that is not the case.
Here's what I'm doing:
config.xml has this line:
<access origin="*" subdomains="true"/>
My service that preforms the simple request is doing:
return $http.get("../data/data.json").then(function (response) {
return response.data;
});
And finally, in my controller, I ask for the service to preform the request:
myService.goGetData().then(onComplete, onError);
In my browser, onComplete() is invoked and on the iPad, onError() is invoked.
Any guidance?
On your local developer machine you're actually running a webserver when you run ionic serve. So a path like ../../data.json will work because it is totally valid in the context of the webserver that has complete filesystem access.
If, however, you try to do the same thing on your device, you're probably going to run into an issue because the device has security policies in place that don't allow ajax to traverse up outside of the root. It is not a dynamic webserver so it can't load files up the tree. Instead you'd use something like the cordova file plugin to grab the file contents from the filesystem. If you prefer, you can use ngCordova to make interacting with the plugin a bit less painful.
I am 99% sure this is what is happening but you can test my theory by pointing your $http call to some dummy .json data hosted on a publicly available server to see if it works. Here is some dummy json data.
Just gonna leave this here because I had the same problem as the original question author. Simply removing any starting slashes from the json file path in the $http.get function solved this problem for me, now loading the json data works both in the browser emulator and on my android device. The root of the $http call url seems to always be the index.html folder no matter where your controller or service is located. So use a path relative from that folder and it should work. like $http.get("data/data.json")
So this is an example json file. save it as data.json
[
{
"Name" : "Sabba",
"City" : "London",
"Country" : "UK"
},
{
"Name" : "Tom",
"City" : "NY",
"Country" : "USA"
}
]
And this this is what a example controller looks like
var app = angular.module('myApp', ['ionic']);
app.controller('ExhibitionTabCtrl', ['$scope', '$http', function($scope,$http) {
$http.get("your/path/from/index/data.json")
.success(function (response)
{
$scope.names = response;
});
}]);
Then in your template make sure you are you are referencing your controller.
<ion-content class="padding" ng-controller="ExhibitionTabCtrl">
You should then be able to use the a expression to get the data
{{ names }}
Hope this helps :)
I was also looking for this and found this question, since there is no real answer to the problem I kept my search on the Internet and found this answer at the Ionic Forum from ozexpert:
var url = "";
if(ionic.Platform.isAndroid()){
url = "/android_asset/www/";
}
I've used it to load a 3D model and its textures.
update: ionic 2 beta (version date 10 Aug 2016)
You must add prefix to local url like this: prefix + 'your/local/resource'.
prefix by platform:
ios = '../www/'
android = '../www/'
browser = ''
we can create an urlResolver provider to do this job.
notice: only change url in *.ts code to access local resource, don's do this with remote url or in html code.
Have fun and good luck with beta version.
An Starter Ioner
It is possible to access local resources using $http.get.
If the json file is located in www/js/data.json. You can access using
js/data.json
Do not use ../js/data.json. Using that only works in the local browser. Use js/data.json will work on both local browser and iOS device for Cordova.

Can the document.location.protocol value be changed in a WinRT webview?

I'm currently having an issue with the WebView control used in a Universal WinRT app (Windows 8.1/Windows Phone 8.1).
I currently load the following piece of JavaScript into the WebView using the NatigateToString method:
<html>
<head>
<base href='MY_BASE_URL'>
</head>
<body>
<script>
var idcomments_acct = 'MY_ACC_ID';
var idcomments_post_id='POST_ID';
var idcomments_post_url='POST_URL';
</script>
<span id='IDCommentsPostTitle' style='display:none'></span>
<script type='text/javascript' src='http://www.intensedebate.com/js/genericCommentWrapperV2.js'></script>
</body>
</html>
This is the piece of code for the IntenseDebate generic install that can be found here.
The issue is with this line of code in the referenced IntenseDebate code:
load_js(document.location.protocol+"//connect.facebook.net/en_US/all.js")
This piece of code fails because document.location.protocol is set to about: in the WebView, leading to a 404 error on this call.
On the Android/iOS webviews simply setting the base URI to a http: or https: based address using their loadDataWithBaseUrl methods worked fine, but the WinRT WebView is missing a similar method. And setting the Base url in the HTML itself (like shown in the piece of code above) does work for resolving image url's and sortlike, but this method doens't change the document.location values.
Since I can't modify the referenced JS file and putting the above piece of HTML on a server isn't an option in this apps usecase, is there any way you can force the document.location.protocol to be a certain value in the WinRT webview? Or is there any other way to get this bit of HTML to work in a webview?
There isn't a direct way to do this. WebView doesn't provide any interface to override the document.location . If branching off of the protocol is a common pattern then this may be a good feature to request on http://wpdev.uservoice.com .
I'm not familiar enough with HTML/JavaScript best practices to say for sure, but most of the references I find searching for document.location.protocol warn against assuming that the protocol will always be http: or https. This may be something that IntenseDebate should fix.
That said, you may be able to get past this by injecting code into your page which finds the problem location in the DOM and changing it live. You can't change just the protocol, but you may be able to find where it is referenced and change that there. I assume it gets loaded into the commentScript.src from genericCommentWrapper2.php referenced in genericCommentWrapper2.cs and then added to the document's head.

Resources