In this post, I’m going to cover the Info.plist changes required to enable your app to locate alternate icons and checking for the availability of alternate icons. I’m also going to provide an Objective-C code sample, and discuss the supportsAlternateIcons API and consider what it might be hinting at. I cover the prompt that users may see when invoking your implementation of this API and how to check if your user is actually using an alternate icon. Finally, we’ll talk about the callback block, and resetting the custom icon back to the original.

I implemented the new icon swapping API available on iOS 10.3, as part of Ultimate Zmanim 11.2’s new Color Themes for premium users. Here’s what I learned:

First, the documentation is a little confusing to read, but if you want to implement this feature, you want to add a dictionary with the CFBundleIcons key to Info.plist. CFBundleIcons has a few sub-keys that you might be familiar with from iOS 6’s Newsstand. (See UINewsstandIcon.) The ones we care about here are CFBundlePrimaryIcon and CFBundleAlternateIcons. Both of these are dictionaries. CFBundlePrimaryIcon contains two keys: A boolean UIPrerenderedIcon and an array with the key CFBundleIconFiles.

To add alternate icons, though, we need to add CFBundleAlternateIcons. The keys in this dictionary are names of icons. Each key contains a dictionary, which matches the contents of the primary icon. That is, each icon key is going to have a UIPrerenderedIcon key and an array of CFBundleIconFiles strings.

A screenshot of info.plist


Note: On tvOS, there are no prerendered icons, so the CFBundleAlternateIcons key simply contains an array of icon file name strings.


The alternate icon files themselves have to be png files in your app bundle. If you put them into an asset catalog, iOS will not find them. (I tried alternate icon entries, and regular image entries. Neither worked.) Remember to name your icons with the retina display naming convention. For example, if my icon file name is AlternateIconBlue, I want to have a dictionary in CFBundleIcons called CFBundleAlternateIcons. On iOS that’s going to have a key called AlternativeIconBlue with UIPrerenderedIcon (set to YES in my case) and then an array called CFBundleIconFiles containing AlternativeIconBlue again. (On tvOS, it’s a little simpler, as noted above.)


Steve Troughton Smith has a great demo project on Github illustrating how it all works.


Lastly, remember that this API is new to iOS 10.3, and using it without checking for availability is going to crash your app. There’s an API check to use for this, and then there’s a way to check for the API. First, check for the supportsAlternateIcons property on UIApplication, and then use the property itself to ensure that your app supports alternate icons.


  
- (void)setAppIconToUseIconWithFilename:(NSString *)appIconFileName
  if ([[UIApplication sharedApplication] respondsToSelector:NSSelectorFromString(@"supportsAlternateIcons")])
    {
        if (UIApplication.sharedApplication.supportsAlternateIcons)
        {
            [[UIApplication sharedApplication] setAlternateIconName:appIconFileName completionHandler:^(NSError * _Nullable error) {
                if (error) {
                    NSLog(@"Filename: %@, Error swapping icons: %@", appIconFileName, error);
                }
            }];
        }
    }
}

I’m unsure why we need supportsAlternateIcons, since iOS 10.3 and tvOS 10.2 both support alternate icons. App extensions do not support this API, but setAlternateIconName:completionHandler: is already marked as disallowed by the API. We also already have runtime checks to account for the availability of methods and properties. Perhaps this is useful future-proofing if Apple introduces other iOS-based environments that might not support alternate icons. That’s the best explanation I can think of, but if you have an additional perspective, let me know on Twitter.

This code worked for me, and oddly, didn’t prompt me during my testing. Most developers report seeing an alert informing the user that they changed the icon using this API. I don’t know if it’s a development thing, but I’m planning to test this by installing the app over TestFlight and trying it out.

Lots of use cases depend on this alert being suppressed for a seamless experience, but the user-comfort argument is a pretty strong one. If apps change icons and don’t tell the user, that can get pretty annoying pretty fast.

I noticed a few things while looking at UIApplication.h. There’s a property to check if your app is using an alternate icon. You can check by querying UIApplication’s alternateIconName property. If it’s nil, that means your app is using the default icon. That’s a neat trick.

Also, “the completion handler will be invoked asynchronously on an arbitrary background queue; be sure to dispatch back to the main queue before doing any further UI work.” I think this is a fairly common pattern at this point. Most of Apple’s privacy alerts that I’ve seen have similar behavior. It’s a simple thing to account for, but having the documentation confirm is important.

One more thing, if you want to reset your app icon to the default icon, you can pass nil into setAlternateIconName:completionHandler: and UIKit will clear that alternateIconName and reset your app’s icon to its original asset-catelog based icon.

Thanks for reading, and happy coding.