How did an image make our APP crash

Recently, our team encountered a special case which drove us crazy. Before we released our new version app, everything looked great. But in the next few hours, I found I received lots of warning letters continuously and seemed that something bad happened… Oh! No! Each of them is crash report and forces we to enter “on duty” mode. Here is the detail…

At the beginning of our survey, we first check each different call stack threads in crash report. None of them is easy to be resolved and looks like every crash is happened in different portion of code. However, they have some common characteristics which is related to Auto Layout and Image. We continue to check these reports, and find another clue: These users are using iOS 9.2! I think it is not a random crash case. We change our way, and start to find related issues through internet.

Call stack in Crashlytics report

I search with keywords: iOS9.2, crash, FBSSerialQueue, and find below link discussing about the crash after being released through App Store and TestFlight. It also draws our attention that this issue only happens if app is downloaded through App Store.

“ERROR ITMS-90682: Invalid Bundle — The asset catalog at ‘Payload/XXXXX/’ can’t contain 16-bit or P3 assets if the app supports iOS 8 or earlier.” is possible the same issue as we met before. However, we didn’t see this error when submitting the app to iTunes Connect.

The detail is Apple allows developer to use extended RGB color with 16-bit or P3 assets as image format after iOS 9.3 version. But those apps which are lower than iOS 9.3 version will result in unpredictable error. At this time, we know what happen in our app. We recheck each line in code, and those code which relates to AutoLayout are tuning the image position. So the question is exposed and user who uses iOS 9.0 to 9.2 will crash immediately after opening our product.

Here comes two different paths we have to survey: How to resolve this problem? and How to prevent it happen again?

First question is easy to resolve and the latter one needs some tricks to do. We can go back to the link and find another discussion thread:

The basic concept of detecting image is to unarchive the ipa file, and find out file which we should convert it to JSON file.

We unarchive ipa file and transfer into JSON file using below code.

xcrun --sdk iphoneos assetutil --info ./ > ./assets.json

The detail of JSON file shows every image assets we use in our app and JSON file is something like this:

"StorageVersion" : 14,
"CoreUIVersion" : 492,
"PlatformVersion" : "9.0",
"AuthoringTool" : "@(#)PROGRAM:CoreThemeDefinition PROJECT:CoreThemeDefinition-338.3\n",
"Key Format" : [
"DumpToolVersion" : 492.2,
"MainVersion" : "@(#)PROGRAM:CoreUI PROJECT:CoreUI-492.2\n",
"AssetStorageVersion" : "IBCocoaTouchImageCatalogTool-9.2",
"SchemaVersion" : 2,
"Platform" : "ios"
"Height" : 12,
"Colorspace" : "srgb",
"RenditionName" : "registered_label",
"AssetType" : "Vector",
"SizeOnDisk" : 5025,
"Name" : "Library_Registered",
"Idiom" : "universal",
"Width" : 16
"AssetType" : "Image",
"BitsPerComponent" : 16,
"ColorModel" : "RGB",
"Colorspace" : "extended SRB",
"Compression" : "lzfse",
"DisplayGamut" : "P3",
"Encoding" : "ARGB-16",
"Idiom" : "universal",
"Image Type" : "kCoreThemeOnePartScale",
"Name" : "Intro_Background",
"Opaque" : false,
"PixelHeight" : 812,
"PixelWidth" : 375,
"RenditionName" : "bg_intro_first.pdf",
"Scale" : 1,
"SizeOnDisk" : 388200

Each of dictionary represents a image metadata. We can see one of JSON dictionary has P3 DisplayGamut property which will result in crash if releasing this app in the App Store. The appropriate way to do is just replace this image with newer one which has sRGB with 8-bit, and upload the app to App Store. Now, everything will be fine!

How to prevent it happen again?

So far, we know how to detect the image format. We can move these code into our continuous integration (CI) flow. Here is our implementation in Jenkins script:

#!/bin/bash -l
function checkImageIsValid()
rm -rf ./checkImageDir/
mkdir checkImageDir
unzip "${XcodeTarget}_${version}_${BUILD_NUMBER}_${ProductPostfix}.ipa" -d ./checkImageDir/
xcrun --sdk iphoneos assetutil --info "./checkImageDir/Payload/${XcodeTarget}.app/" > ./checkImageDir/Assets.json
counter=`grep -rn '"DisplayGamut" : "P3"' ./checkImageDir/Assets.json | wc -l`
if [[ "$counter" -gt 0 ]]; then
echo "find ${counter} of errors in image."
exit 1

Each time we start to build app will erase temporary directory first to prevent Jenkins system from using outdated data. Then we start to create temporary, unarchive our ipa file which just created before (you can use fastlane tool or build and archive by yourself ) and transfer the file to Assets.json file.

We count the line of "DisplayGamut": “P3”appearances time, if the counter is larger than 0, then app bundle has error image which should be fixed immediately.

counter=`grep -rn '"DisplayGamut" : "P3"' ./checkImageDir/Assets.json | wc -l`

So we just exit this job using exit 1 if found error. It’s pretty simple and can prevent side effect if designer and developer does not notice this.

This issue only occurs after being released through App Store, but we can reproduce crash issue through TestFlight. It is the only way we can pre-check before submitting APP to App Store. The idea is we first upload the App with image issue and release it using internal beta test mode, then we fix the issue and upload again to check the crash is resolved. That’s all!!

In conclusion, sometimes developer or QA cannot find every bug or issue in development phrase, but it is still necessary to have instant response after launching the new feature.

Further Reading: