We have had a rough couple of weeks while we were trying to fix iOS crashes. I mentioned this briefly in my other post, but I wanted to jot down my experiences with iOS 7 which may be of help to anyone who's still banging their heads against the wall like we did. The crashes seemed to popup out of nowhere and it took us some head-banging to figure out how to work around them. A couple of thoughts:
- If iPads or iPhones start crashing your web application, you have probably already found your low memory reports. Even though Blendle was functioning just fine on desktops, Android devices and even Windows Mobile, it is very much possible that iOS slaps your application for using too much memory without touching iOS bugs. This is not a fixed limit, but depends on the free memory available on the device. I think this is a good thing, as long as we as developers are able to monitor usage (which we are not).
- We started by using the Chrome Developer Tools and discovered a whole bunch of memory leaks. We fixed most of the issues by refactoring our code and started to take more care with our event listeners. Moving away from jQuery was also a great improvement (because we don’t support IE9 and Firefox Old, using native API’s has become surprisingly easy. This is all good, but this didn’t fix the crashes. It created a bit more headroom, but wasn’t nearly enough.
- Next thing we tried was using the image replacing trick. What we did was swapping out our images for a one pixel gif as soon as the image was no longer visible. This should enable Safari to free up the memory used by the image. This didn't fix our crashes. I ended up removing this when implementing the remove DOM nodes solution.
- I had a feeling that we had one or more corrupted fonts that were causing these crashes. Spent some time using font validators to check our fonts (recounted them, we currently have 90 different variants). Disabled all webfonts, still crashes on iPhone and iPad. Crossed that off my list.
- The next big step was to remove nodes from DOM as they weren’t needed at the moment. This is done at two points. First: as you read an item, our framework will remove all the major views that are below the item overlay. This is noticeable when closing an item (on iPad only), as the items have to be added to the DOM and painted again . This made a big impact, but the iPad still crashed as soon as you tried to reach the end of the timeline or the end of long issues. This was fixed by keeping track of where you are in the timeline. All items that are not within a certain range of your viewport are removed from the DOM as well (even though the elements are still in memory, removing them from the DOM apparently frees up enough memory to keep things going for a while). Still, the crashes were not completely fixed. A lot of this nifty approach was taken from an amazing post from the LinkedIn engineering blog and @aarondeklager came up with a proof of concept.
- I read posts from people that complained about crashing Safari’s when using specific combinations of CSS. Specifically animations, specific transitions and transforms. I removed most of these (I’m assuming that adding a transform will trigger some kind of copy to internal buffers which causes iOS to keep stacking up memory). No more crashes. Bingo.
These are the CSS changes I eventually made to stop the crashes (of course, it is an accumulation of fixes, but adding these lines of CSS back in as an experiment proves that this played the biggest role in Blendle being unstable on iPhone and iPad):
- Most of our views consist of multiple tiles. Each tile has a left and top transition to make smooth animations when adding or removing tiles on the go. These CSS transitions are not added on iOS devices anymore
- On Chrome, we had some issues with tiles that were rendered but still invisible. To fix this, we added translateZ(0) to the tiles. This was updated so it would just do this in Chrome for desktop (excluding Chrome for iOS).
- Our dialogs and overlays appeared with a scaling-in animation (transform(translateY(20%) scale(0.5))). These animations are removed for all devices for now.
- If Safari crashes, it is possible that your cookies have not been written to permanent storage. This is very frustrating for users, because not only they are thrown back to their homescreen, if they want to get back they have to log in again
- There is no way to monitor memory usage on Safari like in Chrome (Performance.memory is awesome)
- There is no way to know if Safari did crash in a previous session. Also, the Safari debugger quits as soon as the browser on the iPad or iPhone crashes, which is a real pain in the neck
- Try to quickly eliminate parts that could cause crashes by disabling them (I spent too much time on individual fonts before testing without them altogether)
- Set up small proof of concepts to try and reproduce crashes
- Make tools like XCode Instruments and Chrome Developer Tools your dearest friends