iOS 9.3.2 Custom URL Scheme not launching app from Safari - javascript

My web page detects the OS and browser, and in the case of iOS Safari will launch my app using a custom URL scheme.
It works fine on my test devices, but I'm seeing an issue with a user using Safari/9.0 on iOS/9.3.2 - the link simply does nothing!
Are custom URL schemes no longer supported? Do I need to start using universal links instead?
For those interested, here is the Javascript code I use in iOS browsers (which is working 99% of the time):
var timer;
var heartbeat;
var lastInterval;
window.addEventListener("pageshow", function(evt){
clearTimers();
}, false);
window.addEventListener("pagehide", function(evt){
clearTimers();
}, false);
function getTime() {
return (new Date()).getTime();
}
// For all other browsers except Safari (which do not support pageshow and pagehide properly)
function intervalHeartbeat()
{
var now = getTime();
var diff = now - lastInterval - 200;
lastInterval = now;
if(diff > 1000)
{ // don't trigger on small stutters less than 1000ms
clearTimers();
}
}
function clearTimers()
{
clearTimeout(timer);
clearTimeout(heartbeat);
}
function intervalHeartbeat()
{
if (document.webkitHidden || document.hidden)
{
clearTimers();
}
}
function launch()
{
lastInterval = getTime();
heartbeat = setInterval(intervalHeartbeat, 200);
timer = setTimeout(function ()
{
logErrorToMyServer();
}, 2000);
//Launch app via custom URL scheme
window.location = "myapp://";
}

Custom URI schemes have been a not-good option since the introduction of iOS 9.2. Apple has definitely made it clear that Universal Links are the preferred approach to deep linking.
I'm not aware of any retroactive changes that would be causing Safari on 9.0 - 9.3.2 to do nothing in this situation (you should at least be getting an error pop-up), but this will continue to be unsupported for the foreseeable future and you should get Universal Links up and running as soon as possible. More details available in this blog post.

Related

Web based Timer application using window.setInterval method in JavaScript does not work as expected on mobile browser (Chrome, Firefox etc.)

Web-based timer application using window.setInterval method in JavaScript does not work as expected in the mobile browser. The reason behind this is that after a certain time, the display goes off due to user inactivity and an application is moved to the background for power saving. Due to which window.setInterval does not trigger periodically and the entire calculation goes for a toss.
However, I have noticed that certain web-based timer works fine in the mobile browser despite these limitations. E.g. Google Timer. Despite the above limitation Google Timer works fine even in offline mode. What techniques are used by these applications so they work on mobile browser as well.
You need to use system time in the calculations. That way when your page comes forth again it’ll show status based on current time instead of relying on past steps.
btn.addEventListener('click', () => {
btn.disabled = true;
const start = new Date().getTime();
const end = input.value * 60 * 1000 + start;
let timer = setInterval(update, 1000);
update();
function update() {
const current = new Date().getTime();
let remaining = (end - current) / 1000;
if (remaining <= 0) {
clearInterval(timer);
btn.disabled = false;
output.textContent = 'Done!'
} else {
output.textContent = `${Math.floor(remaining / 60)}m ${Math.floor(remaining%60)}s`;
}
}
});
<button id="btn">Countdown</button>
<input id="input" type="number" value="2" min=1 max=59 size=5 />mins
<p id="output">0m 0s</p>

How to know that my website is hidden when user locks the ios device? [duplicate]

I was tracking down some ridiculously high load times that my app's javascript reported, and found that Android (and iOS) pause some JavaScript execution when the window is in the background or the display is off.
On Android, I found that I could use the window.onfocus and onblur events to detect when the app was switching to the background (and js execution would soon be paused, at least for new scripts), but I can't find a way to detect when the screen is turned on or off. Is this possible?
(On Safari, I had similar results except that onfocus and onblur didn't fire reliably.)
There is few options to check it:
Using Visibility API
Using focus and blur events to detect browser tab visibility:
window.addEventListener("focus", handleBrowserState.bind(context, true));
window.addEventListener("blur", handleBrowserState.bind(context, false));
function handleBrowserState(isActive){
// do something
}
Using timers, as mentioned above
I just found a pretty good solution for my use case:
function getTime() {
return (new Date()).getTime();
}
var lastInterval = getTime();
function intervalHeartbeat() {
var now = getTime();
var diff = now - lastInterval;
var offBy = diff - 1000; // 1000 = the 1 second delay I was expecting
lastInterval = now;
if(offBy > 100) { // don't trigger on small stutters less than 100ms
console.log('interval heartbeat - off by ' + offBy + 'ms');
}
}
setInterval(intervalHeartbeat, 1000);
When the screen is turned off (or JS is paused for any reason), the next interval is delayed until JS execution resumes. In my code, I can just adjust the timers by the offBy amount and call it good.
In quick testing, this seemed to work well on both Android 4.2.2's browser and Safari on iOS 6.1.3.
Found a nice function here:
http://rakaz.nl/2009/09/iphone-webapps-101-detecting-essential-information-about-your-iphone.html
(function() {
var timestamp = new Date().getTime();
function checkResume() {
var current = new Date().getTime();
if (current - timestamp > 4000) {
var event = document.createEvent("Events");
event.initEvent("resume", true, true);
document.dispatchEvent(event);
}
timestamp = current;
}
window.setInterval(checkResume, 1000);
})();
To register for event:
addEventListener("resume", function() {
alert('Resuming this webapp');
});
This is consistent with Cordova which also fires the resume event.
what will you do in your script once you now that the screen turns off? Well anyway, you can inject Java objects ( http://developer.android.com/reference/android/webkit/WebView.html#addJavascriptInterface(java.lang.Object,%20java.lang.String) ) to interface with the activity and proxy all information you require in JS world.

How to detect chrome extensions whether it is installed in user browser

There are a lot of answers related to this question, but most of those answer information are deprecated years ago by chrome browser.
I need a working example, how to detect google chrome browser addon/extension which is installed in users browser using (javascript/any method).
If i use event detection, there is a addon called " Luminous:
JavaScript events blocker" which blocks all event detection and bypass
the events generated by javascript.
<script>
(function(w, u){
var intervalLuminous = null;
var isLuminousInstalled = false;
var setLuminousDetected = function(){
isLuminousInstalled = true;
alert('Luminous: JavaScript events blocker installed!');
}
var checkLuminous = function(){
if (document.getElementById('luminous-options') || document.getElementById('luminous-data')) {
clearInterval(intervalLuminous);
setLuminousDetected();
}
}
intervalLuminous = setInterval(function(){
checkLuminous();
}, 10);
if(!intervalLuminous){
setLuminousDetected();
}
})(window, undefined);
</script>

Force unsupported browsers to ignore VIBRATION API methods

I am using Vibration API to vibrate user device to improve UX.
navigator.vibrate(200);
The problem is it breaks my website on unsupported browsers/devices. I know I can check for vibration support before calling the vibrate method like this:
if("vibrate" in navigator) {
// vibration API supported
}
But I have already published my website, its live. I require some kind of a hack which will force unsupported browsers/devices to ignore navigator.vibrate() method where ever used.
METHOD 1
if(window.navigator && typeof window.navigator.vibrate !=='undefined')
{
//Execute your code
console.log("Vibrate API is supported by browser",window.navigator.vibrate);
}
else
{
console.log("Browser issues: Vibrate API not supported");
}
MEHTOD 2
try
{
console.log("Vibrate API is supported by browser",window.navigator.vibrate);
//Execute your code
}
catch(Err)
{
console.log("Browser issues:",Err);
}
As suggested by Keith
These 4 lines (kept in global scope) will stop unsupported browsers/devices from breaking your site.
let mainNavigator = window.navigator;
let navigator = {};
let vibrationSupport = "vibrate" in mainNavigator;
vibrationSupport ? navigator.vibrate = function(value) { mainNavigator.vibrate(value); } : navigator.vibrate = function(value) {};

Is there a bug with using InnerHTML inside a UIWebView within a native iPhone application?

I have a fairly large HTML/JS/CSS application that works great when running as a web application with Safari on the iPhone.
When running this same application in an UIWebView within a native iPhone application calls within jQuery to create HTML fragments fail silently (ie: $("<div>HELLO WORLD</div>"); will not create the element.
I've tracked this down to the following equivalent code snippet in clean jQuery method:
var div = document.createElement(“div”);
div.innerHTML = “<div>HELLO WORLD</div>”;
When I look at div.outerHTML I see
<div>/<div>
div.innerHTML returns an empty string.
This does not appear to be a jQuery problem, nor does this happen 100% of the time. I haven’t been able to find a pattern, but in some cases it works 2-3 times in a row, sometimes if fails 5-6 times consecutively. This seems to only shows up when running the application inside a UIWebView in an Objective-C application. Also I’ve only seen this on an actual device running iOS 4.2, not the emulator.
Has anyone run into anything similar? Does anyone have a fix?
I had this problems too. It happens when the CPU of the phone is very busy (say 100%). Then the rendering engine sometimes forget about innerHTML settings.
The solution included in my unify project is to test if there is an element in childNodes, otherwise apply it again.
var target = document.createElement("div");
var text = "<div>Hello World</div>";
target.innerHTML = text;
var self = this;
self.__intervalHandle = window.setInterval(function() {
target.innerHTML = text:
if (target.firstChild) {
window.clearInterval(self.__intervalHandle);
self.__intervalHandle = null;
}
}, 100);
This forces the rendering engine to apply the innerHTML to the DOM and gives the rendering engine some time (100 ms in this example, a good value in our tests) to handle it.
Well, the solution [NOT a production quality solution] posted by Sebastian worked, but I couldn’t confirm if CPU load would cause this issue. I generated a lot of background load on the iOS host and couldn’t reproduce this issue.
Upon further investigation, the rendering issue seems to be a side effect of iOS shell canceling the navigation. Once the navigation is canceled by the iOS shell, the rendering engine probably take that as not needing to render more UI [basically doesn’t render anything for a small period].
One way to fix this would be to send commands to iOS shell as hash (#) parameters instead of a URL. This way iOS shell will get the commands and doesn’t need to cancel the navigation. This approach seems to work in the test code below. So, if window.location is set to location1, it alerts “At: 1” and element e2 has no value. And if the window.location is set to location2, it alerts “At: 0” and element e2 has the value.
#Kevin, could you confirm that you were canceling the navigation on iOS host when this behavior happened.
Test Code:
Javascript:
var location1 = "myApp://Test";
var location2 = "#myApp://Test";
$("#change").live("click", function (e) {
var element = document.getElementById("e1");
window.location = location1; var i = 0;
element.innerHTML = "At: " + i;
window.__intervalHandle = window.setInterval(function () {
var html = element.innerHTML;
if (html) {
alert(html);
window.clearInterval(window.__intervalHandle);
window.__intervalHandle = null;
} else {
element.innerHTML = "At: " + ++i;
}
}, 1);
document.getElementById("e2").innerHTML = "Test";
});
iOS pseudo code:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSURL* u = [ request URL];
if( [[u scheme] compare:#"myapp" ] == NSOrderedSame) {
{
return NO; // don’t navigate
}
return YES; // navigate
}
You should take a look at http://blog.techno-barje.fr/post/2010/10/06/UIWebView-secrets-part3-How-to-properly-call-ObjectiveC-from-Javascript.
It turns out that cancelling navigation as part of the -webView:shouldStartLoadWithRequest: delegate method may cause issues with innerHTML. I updated our JS code to use the method recommended in that article and I haven't seen the innerHTML issue crop up in a while.

Categories