In iOS 10.3, Apple launches a cool function so that developers are able to programmatically change the app icon. In Apple's UIApplication API document, there are 3 things worth a glance:

//A Boolean value indicating whether the app is allowed to change its icon. To make it true, we need to set up alternative icons in Info.plist file.
var supportsAlternateIcons: Bool

//The name of the icon being displayed for the app. Notice that it is nil when app is displaying its primary icon.
var alternateIconName: String?

//Changes the app's icon. If we set the icon name to nil, the app will display its primary icon.
func setAlternateIconName(String?, completionHandler: ((Error?) -> Void)? = nil)

1. Prerequisite

  • Set target to minimum iOS 10.3
  • Add default AppIcon in Assets.xcassets
  • Add your Alternate icons under your project directory (assets seems not to work)

2. Setup Info.plist

  • Add Icon files (iOS 5)/CFBundleIcons to the Info.plist
  • Add CFBundleAlternateIcons as a Dictionary, it is used for alternative icons
  • Set 3 dictionaries under CFBundleAlternateIcons, they are correspond to "AppIconAlternate1", "AppIconAlternate2", and "AppIconAlternate3"
  • For each dictionary, create one properties  CFBundleIconFiles as Array. The array value for key CFBundleIconFiles includes the icon files.

Info.plist screenshot

Info.plist source code

<key>CFBundleIcons</key>
<dict>
	<key>CFBundleAlternateIcons</key>
	<dict>
		<key>AppIconAlternate1</key>
		<dict>
			<key>CFBundleIconFiles</key>
			<array>
				<string>AppIconAlternate1</string>
			</array>
		</dict>
		<key>AppIconAlternate2</key>
		<dict>
			<key>CFBundleIconFiles</key>
			<array>
				<string>AppIconAlternate2</string>
			</array>
		</dict>
		<key>AppIconAlternate3</key>
		<dict>
			<key>CFBundleIconFiles</key>
			<array>
				<string>AppIconAlternate3</string>
			</array>
		</dict>
	</dict>
	<key>CFBundlePrimaryIcon</key>
	<dict>
		<key>CFBundleIconFiles</key>
		<array>
			<string></string>
		</array>
		<key>UIPrerenderedIcon</key>
		<false/>
	</dict>
	<key>UINewsstandIcon</key>
	<dict>
		<key>CFBundleIconFiles</key>
		<array>
			<string></string>
		</array>
		<key>UINewsstandBindingEdge</key>
		<string>UINewsstandBindingEdgeLeft</string>
		<key>UINewsstandBindingType</key>
		<string>UINewsstandBindingTypeMagazine</string>
	</dict>
</dict>

3. Code

Let us assume we have 4 buttons in a view. Tap any of them would make the app to display a correspond icon. Here is the sample code:

@IBAction func appIcon1Tapped(_ sender: UIButton) {
    changeIcon(to: "AppIconAlternate1")
}

@IBAction func appIcon2Tapped(_ sender: UIButton) {
    changeIcon(to: "AppIconAlternate2")
}

@IBAction func appIcon3Tapped(_ sender: UIButton) {
    changeIcon(to: "AppIconAlternate3")
}

@IBAction func resetAppIconTapped(_ sender: UIButton) {
    //Set the icon name to nil, the app will display its primary icon.
    changeIcon(to: nil)
}

func changeIcon(to name: String?) {
    //Check if the app supports alternating icons
    guard UIApplication.shared.supportsAlternateIcons else {
        return;
    }
    
    //Change the icon to a specific image with given name
    UIApplication.shared.setAlternateIconName(name) { (error) in
        //After app icon changed, print our error or success message
        if let error = error {
            print("App icon failed to due to \(error.localizedDescription)")
        } else {
            print("App icon changed successfully.")
        }
    }
}

4. Demo Video

Ref- Apple Documentation