Debug slow angular-ui-router state change on Mobile Safari - ios

I'm writing an angular app, using angular-ui-router to manage states / routing.
On desktop browsers (Chrome / Safari) this is working fine. However, on Mobile Safari on IOS 6 on iPhone 4 (and to a lesser extend on IOS 7 on iPhone 5) changing state, via $state.go, can take anything up to 2 seconds.
I'm using ngTouch, so I don't think that it's the 300ms that the native click event takes to fire. ngClick attributes that don't call $state.go now seem to work pretty much instantaneously.
How can I debug this to find where the time is being spent?

ngTouch doesn't work with the ui-sref directive. We used fastclick.js to handle the click behavior, and removed ngTouch. The issue is that the directives step on each others events, and are in fact incompatible. You can see this by reading the implementation of both directives.

Use console.time to print out in the Mobile Safari Console,
https://developer.chrome.com/devtools/docs/console-api#consoletimelabel
...in association with the ui-router listeners :
$stateChangeStart
$stateChangeSuccess
$viewContentLoading
$viewContentLoaded
/* EXAMPLE - INCREMENT THE TIMER
FOR EACH ONE OF THE LISTENERS, TO DETECT THE BOOTLENECK */
// START TIMING NOW
console.time('state_transition');
/*
http://devin-clark.com/what-you-might-not-know-about-chrome-devtools/
*/
$rootScope.$on('$stateChangeSuccess',
function(event, toState, toParams, fromState, fromParams){
console.timeStamp('state_transition');
});
// STOP TIMER
console.timeEnd('state_transition');
http://www.ng-newsletter.com/posts/angular-ui-router.html
Who knows, maybe for improving ui-router's reactivity on mobile devices, FutureState from UI-router's extras could be something worth to explore :
http://christopherthielen.github.io/ui-router-extras/#/future
...but the only way to be sure is to understand the innards of the mobile browsers and their DOM/JS/GPU's management/computing limitations in respect of desktop browsers.
It could be that ng-animate requires too much of a mobile browser by building the "next" template ahead of its appearance into the viewport. And yet it could be something completely trivial.
Please let us know your progress.

Related

VueJs Nuxt PWA not rendering on iphone

My vuejs nuxt portfolio(https://gunjankadu.com/)is not opening on ios phones but working flawlessly on all other devices. On IOS devices only a red spinner is shown and nothing more.
What can be done?
A script "tag.js" took time to load, which I have currently disabled but still not working.
I had a look on your application and found out two problems.
First was about regular expressions, It seems Safari doesn’t support look behind yet. One alternative would be to put the / that comes before in a non-captured group, and then extract only the first group (the content after the / and before the #).
/(?:\/)([^#]+)(?=#*)/
And second is about The minimal-ui viewport property that you can find out more here
Look at all your js/vue files especially in store. And see if any variable used is not imported. Nuxt in SPA mode doesn't detect those kind of errors and surprisingly the UI gets stuck on loading only on iOS. Very weird but scratched my brain over it for a day.

How to fix LightSwitch HTML application becoming unscrollable (more frequent on mobile)

This one is a nightmare to debug since it almost always happens only on a native android device, where it is impossible to inspect the DOM (or is there a way?).
Essentially, once this bug is triggered, no more scrolling is possible in the app until a full refresh is done.
Note: LightSwitch does not support jq-mobile newer than 1.3.2
This issue happens quite more rarely on desktop. But since it finally did, I was able to dig thru the DOM to see what was happening. Once I knew where the issue was, it didn't take long to find a documented bug in jq-mobile-1.3.2 that LightSwitch uses: https://github.com/jquery/jquery-mobile/issues/6515
Root cause: sometimes when LightSwitch refreshes the data, the screen get's stuck in a transitioning mode (jq mobile feature), and transitioning CSS class has a overflow:hidden property set.
The link above describes how to modify jquery-mobile to make transition state more robust, but for the purposes of LightSwitch, just add the following CSS into user-customization.css
.ui-mobile-viewport-transitioning, .ui-mobile-viewport-transitioning .ui-page {
overflow: auto !important;
}

Cordova white screen of death iOS - browserify, react, cordova

I have a strange situation with a project for iOS.
Its created using browserify and React for Cordova and aimed at iOS.
We have built the project for iOS using Cordova commands without any issues. It runs with no xcode errors or Javascript errors either however none of the JS ui appears on screen. We just get a blank white screen with the system bar at the top. See attached screen shot.
So far we have found out that if you add HTML to the index.html in WWW folder it prints that fine so I assume its the JS.
When we run in a browser it works as well as building for Android.
We have no problems with anything other than iOS (simulator and device build).
hopefully someone can help me out here because I'm stumped. Please let me know what files you need to look at and I'll make them available.
Thanks in advance.
IMAGES
The error screen: http://www.voidapplications.co.uk/errorScreen.png
What we expect to be shown: http://www.voidapplications.co.uk/whatWeExpect.png
If you are using internationalization component that's because you need to use the Intl polyfill:
Intl.js and FT Polyfill Service
Intl.js polyfill was recently added to the Polyfill service, which is developed and maintained by a community of contributors led by a team at the Financial Times. It is available thru cdn.polyfill.io domain, which routes traffic through Fastly, which makes it available with global high availability and superb performance no matter where your users are.
To use the Intl polyfill thru the Polyfill service just add one script tag in your page before you load or parse your own JavaScript:
<script src="https://cdn.polyfill.io/v1/polyfill.min.jsfeatures=Intl.~locale.en"></script>
When specifying the features to use thru the polyfill service, you have to specify what locale, or locales to load along with the Intl polyfill for the page to function, in the example above we are specifying Intl.~locale.en, which means only en, but you could do something like this:
<script src="https://cdn.polyfill.io/v1/polyfill.min.js?features=Intl.~locale.fr,Intl.~locale.pt"></script>
note: the example above will load the polyfill with two locale data set, fr and pt.
This is by far the best option to use the Intl polyfill since it will only load the polyfill code and the corresponding locale data when it is really needed (e.g.: safari will get the code and patch the runtime while chrome will get an empty script tag).
source & other ways to include Intl polyfill: https://github.com/andyearnshaw/Intl.js#getting-started
You can debug the UIWebView in the simulator with Safari http://moduscreate.com/enable-remote-web-inspector-in-ios-6/. You'll probably see a big error message in the console, if not you will have access to the debugger so you can step through and sort it out.
I have had this issue in the past, it turned out to be unsupported javascript features. for example, setting default parameters in the function. I.e:
function test( myvalue=0 ) { /* ... */ };
I had to initialise the value inside the function instead. You could also try removing any ecma script 2016 features you may have implemented.
Trouble shooting this type of issue was painful. I created a minimal version of the smallest part of my product compiled and ran it on ios emulator, added another chunk of the product, rinse and repeat.
Im sure there's a better way to detect these issues, but I do not know it.

"Rubber band" events on phonegap app

I'm working on an iOS app using Phonegap/ Cordova and jQuery Mobile.
I want to be able to refresh the data when the user pulls the page down (rubber band effect). I tried using iScroll 4 but it made scrolling my page slow.
I didn't find a phonegap plugin for this. I thought this was a pretty standard iOS feature so maybe I am missing something? Is there any easy way to listen to the pull down event using phonegap / cordova?
Thanks!
Adding this line into CDVViewController.m (Cordova 2.0 or later) is said to work but I've actually never tried it myself as I'm still using iScroll 4. Give it a shot and let me know how it worked for you.
[webView.scrollView setDecelerationRate:UIScrollViewDecelerationRateNormal];
Likewise, however somewhat irrelevant to this matter, the code snippet below is said to reduce the on click delay from 300 ms which is also something phonegap developers usually want. Neither this nor the line above has been properly tested but perhaps a good way to start off.
[webView.scrollView setDelaysContentTouches:NO];

Is History API broken on iOS? (Location bar doesn't update on pushState)

Filing this under the either the I Can't Believe No One Noticed This Before or the I Must Be Missing Something categories:
It appears that if you do a simple window.history.pushState on iOS, the location bar doesn't update unless it is in response to a user gesture. The state itself does get pushed (as you can see by hitting the back button button).
Here's is the tiniest test-case I could come up with recreate the issue:
http://thelink.is/history-api-ios-bug
On a desktop browser that supports the History API, you should see the URL in the location bar change to /0, /1, etc., every second. On iOS – tested with iPhone (running iOS 4.3) and iPad (running iOS 4.3.3) – the location bar doesn't update but hitting the back button will take you the correct previous location (which will 404 on the test-case since there's no back-end logic to handle those URLs).
Thoughts? Workarounds? A shoulder to cry on and hugs?
UPDATE: this issue was fixed in iOS 5.
So the bottom line is that iOS has added its own security around the history API, meaning that you can't use script to change the url. Only a user action can allow the history API to change the url - i.e. a click - as per Aral's example.
The workaround is to uses a hash (aka fragment identifier) on the url.
Instead of the history.pushState we'll just change the location:
var i = 0;
var locationUpdateInterval = setInterval(function(){
window.location.hash = i;
i++;
}, 1000);
To capture the event either when something changes the that location in the iOS app or if they have permalink to a particular page/panel in your app:
// named function on purpose for later
function hashchange() {
var pageId = location.hash.substr(1); // drop the # symbol
// do something with pageId
}
window.onhashchange = hashchange;
// onload - if there's a hash on the url, try to do something with it
if (location.hash) hashchange();
It's pretty poor that we can't use the pushState/popState on iOS, but it's the same security as not being able to trigger fullscreen video unless the user initiates the action, which is the same as downloading video or audio content on iOS - you can't script it, the user must start it (somehow or another).
Just as a note about Android - the problems are pretty similar, so this (should) also work as a workaround for Android.
If you want desktop support, most browsers support onhashchange but, yep, you guessed, IE is lacking behind - so you can polyfill that bad boy in (though requires jQuery...): http://benalman.com/projects/jquery-hashchange-plugin/
Hope that helps.
Works fine for me when using: https://github.com/browserstate/history.js - this also fixes many other cross browser bugs with the HTML5 History API.
As of v1.7, here are the bugs it solves:
History.js solves the following browser bugs:
HTML5 Browsers
Chrome 8 sometimes does not contain the correct state data when traversing back to the initial state
Safari 5, Safari iOS 4 and Firefox 3 and 4 do not fire the onhashchange event when the page is loaded with a hash
Safari 5 and Safari iOS 4 do not fire the onpopstate event when the hash has changed unlike the other browsers
Safari 5 and Safari iOS 4 fail to return to the correct state once a hash is replaced by a replaceState call / bug report
Safari 5 and Safari iOS 4 sometimes fail to apply the state change under busy conditions / bug report
Google Chrome 8,9,10 and Firefox 4 prior to the RC will always fire onpopstate once the page has loaded / change recommendation
Safari iOS 4.0, 4.1, 4.2 have a working HTML5 History API - although the actual back buttons of the browsers do not work, therefore we treat them as HTML4 browsers
None of the HTML5 browsers actually utilise the title argument to the pushState and replaceState calls
HTML4 Browsers
Old browsers like MSIE 6,7 and Firefox 2 do not have a onhashchange event
MSIE 6 and 7 sometimes do not apply a hash even it was told to (requiring a second call to the apply function)
Non-Opera HTML4 browsers sometimes do not apply the hash when the hash is not urlencoded
All Browsers
State data and titles do not persist once the site is left and then returned (includes page refreshes)
State titles are never applied to the document.title
(Update: Just saw that Remy answered too – read his in-depth answer, above, instead.)
Remy provided a workaround for this issue on Twitter.
Basically, if you change the location.hash, the address in the location bar updates. However, this does create a separate entry in the history (which doesn't work for what I'm trying to achieve). The workaround I'm implementing is to use hash-bang URLs for iOS and regular ones for other platforms until the iOS bug is fixed. This is definitely not ideal and I hope that Mobile Safari on iOS will start behaving like Chrome, Firefox and Safari on desktop.
Here's what I found:
When the pushed location contains a hash symbol, the adressbar will be updated.
So this will work:
window.history.pushState(data, title, 'a/new/url#');
But the window.location object will not be updated so you need to save the pushed url into a variable and use that instead of window.location if you need the pushed location.
Tested on Safari for Android.
I found a hack that kinda works. It turns out that if you change the hash right after history.pushState the location bar gets updated. Like:
window.history.pushState(data, title, 'a/new/url');
window.location.hash = 'new';
changes the location bar to http://example.com/a/new/url#new. Which introduces another problem because the hash becomes it's own history entry. So you'll need to listen to onHashChange anyway.
It's a bit complicated, but there are some people who really, really hate hashbang urls and are very vocal about it. So it's worth it.

Resources