“Is it supposed to look like this?”
Those words were uttered by my wife after I had loaded the latest test version of my app on to a phone so I could demonstrate a new feature. She had wandered off into another area of the app, and as I peaked over her shoulder, I winced in fear as indeed, the screen should not look like that, but nevertheless contained big splotches of transparent pixels where the keyboard was supposed to be. She was trying to type in the name of an item of food but was struggling when half of the keys where randomly invisible.
There are moments in software development when you encounter bugs so strange and bizarre that you have no explanation for their cause, that you cannot even conceive of how to achieve the effect if you were trying to on purpose. This was one of the those moments – how on earth do you, at an application level, render chunks of the keyboard to disappear, and different chunks became invisible when pressing different keys, and sometimes those chunks flashed at you, tantalizing and teasing letters?
Of course everything works perfectly on the simulator, just not on the device, and to add insult to the misery, the splotches were ghosts, as grabbing a screenshot failed to include the corruption. I had to resort to using a second camera to capture their existence.
I am on the verge of completing a new app, to be named MealSchedule, that allows you to plan out the dishes you want to eat at future meals. I envisage the app as a reminder of what I’ll be cooking that day and to guide my shopping — a future version will contain some ingredient/shopping list component. I had started the app as a proof of concept and to try out a few UI features, but I had grown to like the direction it was heading in, so I was now focused on launching to the app store. Except there were transparent areas on the screen where they shouldn’t be, and I had no explanation of why.
The main fear, of course, is that this was an internal bug, a problem in iOS that only Apple could fix, a bug which would have an indeterminate timeline and therefore your whole app is scuppered before it can even launch. I’m sure my wife could sense my nervousness as she handed the phone back to me and left me alone to stew.
So first steps, try and isolate the problem. This was the only data entry field in the app, it is UITextView (because I needed multiple lines), but it was wrapped up in a third party control. Let’s quickly rip that out and try a plain UITextView… Hmmm, that works sometimes but the holes return when I use a large font size than the default. That’s odd, feels fishy, and doesn’t make any sense. I try several other things and continue to get inconsistent results. I can’t spot any discernable pattern.
The UITextView is on a child view controller that is presented from the main screen. Let’s try dropping an instance on to the main screen (ignore the aesthetics for now). Great, that works perfectly, even the HPGrowingText version. Here was a clue, but it was late, I was tired and I’d had a couple of glasses of wine, to spot it amongst the raft of commented out code and quick hacks.
The next day, refreshed and raring to go because this was a do-or-die bug, I tried a different tack. The app worked perfectly on the simulator, but I had only pushed distinct builds to the device and those earlier versions had not exhibited any corrupt keyboard problems. Therefore I could analyze all the recent changes to determine where the problem was introduced and design a workaround or fix from there, only I wasn’t that sure exactly which build was previously on the phone.
Version control for a one person shop may seem like overkill, but I had been pretty good at committing after each discrete piece of functionality (64 commits in 6 weeks), so I had some pretty good fidelity on changes. Slowly by checking out each previous version and undoing each change, I eventually hit upon the one line — notice how all really strange bugs come down to one line — that caused the corruption or not.
The problematic line?
A simple instruction telling a view to not draw any content outside of its bounds. Generally something that shouldn’t cause a problem, and while this particular view is an ancestor view of the UITextView, its bounds in this particular instance, were the entire screen and the keyboard was only ever corrupted on the first two rows of letters and not at the bottom. However the one distinguishing feature about this
_popoverView was that it also had a shadow. Commenting out the shadow and leaving the clipping in, and the corruption did not appear. The shadow was important, and
_popoverView had a subview that I could easily apply the clipping to, all the angst and fear could be addressed in a simple one line change.
So the morals of this story are:
- Use version control and commit often, even if it is a personal project
- Test on the device as often as possible, if for no other reason that you have some history. I had, potentially, about a week’s worth of changes to go through since I had last installed on the device
- Don’t clip a view that has a shadow!