Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to detect in iOS webapp when switching back to Safari from background?

Tags:

How can I build a webpage which is able to monitor when the page gets the focus, especially when Safari is in the background and the user switches Safari back to the foreground.

The code below does not fire the event when switching to Safari on an iPhone

<html>   <head>     <script type="text/javascript">       window.onfocus = function() { alert("onfocus"); };     </script>   </head>   <body>      Main text    </body> </html> 

According http://www.quirksmode.org/dom/events/index.html : Safari iPhone does not fire the event when the window gains the focus.

So my question is still: how to detect by using Javascript on a web page within Safari for iPhone that the window receives the focus?

like image 373
Axel Derks Avatar asked Jan 11 '11 10:01

Axel Derks


People also ask

Who takes the background of Safari on iPhone background?

Open the Safari app and open a new tab. Scroll down to the bottom of the tab — past your favorites, shared articles, reading list — and tap on the “Edit” button centered at the bottom of the screen. At the bottom of the customizations list, tap the “Background Image” slider to enable it.

What happens when app goes background iOS?

It will be resumed when the application is brought back to the foreground. However, in the latter case you should still be prepared for your application to be terminated at any time while it's in the background by cleaning things up on your way to the background.

How do I know if apps are running in the background iOS?

Go to Settings>General>Background App Refresh and you can see what other apps are allowed to update data in the background. iOS dynamically manages memory without any user intervention. The only apps that are really running in the background are music or navigation apps.

Does Safari work in the background?

Open Website Tabs in Background in Safari Browser on iPhoneAll that is required to force Safari browser to open New Tabs in background is to go to Settings > Safari > scroll down and tap on Open Links option. On the next screen, select In background option and tap on < Safari to save the change that you just made.


2 Answers

I believe timers (setInterval()) are suspended when the app enters the background. You could do something like:

var lastFired = new Date().getTime(); setInterval(function() {     now = new Date().getTime();     if(now - lastFired > 5000) {//if it's been more than 5 seconds         alert("onfocus");     }     lastFired = now; }, 500); 

You may need to adjust those time intervals to suite your needs.

But, most likely, if it has been long enough to need a refresh (a few days) safari will probably reload the page because it is out of memory.

like image 179
David Beck Avatar answered Oct 18 '22 04:10

David Beck


Depending on what you need to support, you need a variety of different techniques to detect when a page becomes visible. Variations occur due to browser vendor, browser version, OS, running within WebView/UIWebView/WKWebView, etc.

You can view which events are occurring by using this page. I have found that to detect when the page "wakes up" on all combinations I needed to register all of the following events:

  • window visibilitychange event
  • window focus event
  • window pageshow event
  • starting a timer and seeing if the timer took much longer than it should have (timers are put to sleep by iOS when hibernated). An App using UIWebView doesn't fire the visibilityChange event even on iOS9 (WKWebView is OK).

I used to use webkitRequestAnimationFrame as well, but I removed that because it could cause jank (AFAIK the rendering engine does a blocking call to the main thread for it).

Things to try:

  • Go to another tab
  • Lock screen, wait, unlock
  • Bring another app to focus
  • Minimise browser

You can see which events are happening:

  • in real time by looking at the console log (attach debugger).
  • in real time on a device by using http://output.jsbin.com/rinece#http://localhost:80/ and see the log get requested as Ajax calls (use a proxy, or run a small server on the address after the # and log the body to console).
  • look at the on screen log, and pay close attention to the time logged for each entry to see if the entry was logged e.g. visibilitychange hide event might not occur when page is actually hidden (if app is hibernated), but instead is queued and occurs when page is reshown!!!

iOS: beware if using a timer to detect if an iOS UIWebView has gone to sleep, you need to measure the difference using new Date.getNow() and not performance.now(). That is because performance.now() stops counting time when the page is put to sleep also iOS was slow to implement performance.now()... (Aside: you may be able to measure the amount of time the page was asleep for by detecting the discrepency of differences for new Date.getNow() and performance.now(). Look for the != on the test page).

If you are using UIWebView then there are two techniques that work (You must use UIWebViewif you support an iOS7 App). WKWebView has the visibilitychange event so workarounds are not required.

==Technique 1.

When the applicationWillEnterForeground event occurs in the app, call UIWebView stringByEvaluatingJavaScriptFromString to call your JavaScript pageAwakened().

Benefits: clean, accurate.

Downside: needs Objective-C code. Called function needs to be accessable from global scope.

==Technique 2.

Use webkitRequestAnimationFrame and detect a time lag.

Benefits: JavaScript only. Works for mobile Safari on iOS7.

Downside: ugly risk of jank and using webkitRequestAnimationFrame is a severe hack.

// iOS specific workaround to detect if Mobile App comes back to focus. UIWebView and old iOS don't fire any of: window.onvisibilitychange, window.onfocus, window.onpageshow function iosWakeDetect() {     function requestAnimationFrameCallback() {         webkitRequestAnimationFrame(function() {             // Can't use timestamp from webkitRequestAnimationFrame callback, because timestamp is not incremented while app is in background. Instead use UTC time. Also can't use performance.now() for same reason.             var thisTime = (new Date).getTime();             if (lastTime && (thisTime - lastTime) > 60000) {    // one minute                 // Very important not to hold up browser within webkitRequestAnimationFrame() or reference any DOM - zero timeout so shoved into event queue                 setTimeout(pageAwakened, 0);             }             lastTime = thisTime;             requestAnimationFrameCallback();         });     }     var lastTime;     if (/^iPhone|^iPad|^iPod/.test(navigator.platform) && !window.indexedDB && window.webkitRequestAnimationFrame) {    // indexedDB sniff: it is missing in UIWebView         requestAnimationFrameCallback();     } }  function pageAwakened() {     // add code here to remove duplicate events. Check !document.hidden if supported };  window.addEventListener('focus', pageAwakened); window.addEventListener('pageshow', pageAwakened); window.addEventListener('visibilitychange', function() {     !document.hidden && pageAwakened(); }); 
like image 20
robocat Avatar answered Oct 18 '22 06:10

robocat