Saturday, February 15, 2014

Error: ImageIO: PNG Not a PNG file

<Error>: ImageIO: PNG Not a PNG file

Does your iOS app log the above message and fail to render an image? Do you feel completely befuddled? You're in the right place.

I recently spent a lot of time debugging a problem where an UIImage vanished after doing a perfect job of rendering. Reproducing the bug required four big steps to reproduce. The steps involved around thirty classes total. There were view controllers, file operations, and many views. Stepping through the code in the debugger took ages.

If you need an excuse for removing unnecessary log statements from your app, consider this. The whole time I was debugging the app, the ImageIO log message was hiding among the normal verbose messages in my app. If my app logged zero messages in normal operation, I would have noticed a new one much sooner.

But why did my image disappear? To begin at the end, the log message was 100% accurate. The PNG file wasn't a PNG file. It was a JPEG.

But how did the UIImage ever render to the screen if it tried to open a JPEG as a PNG? It didn't. It opened a PNG as a PNG. Of course.

But at some point the app wrote over the PNG with the same image in JPEG format. If you read the documentation for + (UIImage *)imageWithContentsOfFile: you'll see the phrase that made me smack my forehead:

"does not cache the image"

Oh snap. So our app opened the image, displayed it, and then at some point decided to save space by re-writing the file as a JPEG. But the UIImage was still open. In the next run through drawRect:, the UIImage went back to the file only to discover that its PNG wasn't a PNG. And so the image did the only think it knew how to do: vanish.

The fix I decided to use was to replace the UIImage when the file changed. Now to prune those NSLog()s from the app!