The mystery of the missing iOS app icon

Visual Studio Xamarin Uno Platform

4 years ago

Last week I received a message from Fons Sonnemans who is porting his awesome Minesweeper 10 game from UWP to Uno Platform. He said he was facing a weird issue with his app on iOS, where the app icon was not showing up after deploying. He also posted a screenshot of the issue on Twitter: I originally thought it could be caused by a low iOS version target (which has caused me issues before in other things), but surprisingly it was not that. In the Visual Studio asset editor and Info.plist all seemed perfectly fine. Even the .csproj contained all the <ImageAsset> references:

Even then, the Simulator still kept showing the same old default iOS app icon. I asked Fons if he would be willing to provide me access to the repo so that I could run it locally on my Mac, to check if it is not some kind of caching issue – but that wasn't the case either and the icon was still missing. Let's solve this mystery together!

Suspicious build output

After a while searching the internet I came onto this Stack Overflow answer by mamcx for a similar problem (emphasis mine):

Did you check the build warnings when you compiled the app. You should see a bunch of warnings about missing .png files in the AppIcon image set.

Unlike Xcode, VS requires a reference to each iOS asset .png file in the .csproj file as well as the Assets.xcassets Contents.json file. It's redundant; but it's just how VS works.

At first glance, this seemed to lead nowhere, as I previously confirmed the <ImageAsset> references were, in fact, present in the .csproj file. But out of curiosity I made a search for .png in the Build Output window and found 18 messages like this:

The file "iphone_60x60@2x.png" for the image set "AppIcons" does not exist.

Why would the file not exist? It is clearly visible in the asset editor and it is in the .csproj – something really funky is going on here!

Include those files!

If Visual Studio thinks the image files are missing, let's include them "by force"! In Solution Explorer, I clicked the Show All Files button on the top. There I saw the Media.xcassets folder and right-clicked it and selected Include In Project. What I got were now essentially two copies of the same images in the project – once as part as the Asset Catalogs and once in the Media.xcassets folder:

Two copies now

Two copies now

I thought this can't change anything, but went ahead and tried to run the app again, just to be sure. To my surprise – it worked! The app icon was now there, smiling at me from the iPad's home screen:

Huh, it works?

Huh, it works?

But... why?

It was great it now worked, but why? Luckily, the answer is really close now. I opened the .csproj file to see what is in there. At first glance it seemed Visual Studio just included the same files twice, just without the <Visible>false</Visible> modifier, so the new copy was actually visible in the Solution Explorer.

Looks the same... but what if we look at the two side by side?

Aha! ipad vs iPad! Could it be the uppercase? The Contents.json file referenced the lowercase versions of the image files, but .csproj originally used the one with uppercase "P" in the filenames. This would normally not be a problem, as Windows is case insensitive, but macOS/iOS is not! It turns out that while copying the project files to the output, Visual Studio uses the casing specified in the .csproj file, not the one which the files had originally. I confirmed this by going into the build cache folder on Mac (/User/Library/Caches/Xamarin/mtbs/builds):

Output casing matches .csproj

Output casing matches .csproj

I deleted the build cache, reverted all changes, and then modified the .csproj file to use lowercase versions of the file names in <ImageAsset> elements. Rebuilt and deployed and app icon was there again – but, as the masked magician would say, now we know the secrets!

Now we know the secrets!

Now we know the secrets!

Mystery solved!

The main takeaway I have from this puzzle is that file casing matters when building cross-platform apps, especially because both Android and iOS are Unix-like and are case sensitive. Also, I will try to remember to read the build output warning messages more closely, as they were telling the truth all along!