June 16, 2021
  • Tutorials
  • App Icon
  • Capacitor

How to change your iOS App icon with Capacitor

Simon Grimm

This is a guest post from Simon Grimm, Ionic Insider and educator at the Ionic Academy. Simon also created the Practical Ionic book, a guide to building real world Ionic applications with Capacitor and Firebase.

You have heard that Capacitor makes it easy to access native device functionality with a simplified API — but does it really work for core native functionalities like changing the app icon on your home screen?

In this tutorial we will implement the functionality to switch your app icon on iOS using one of the Capacitor community plugins, created by John Borges, a member of our community!

Capacitor change icon example

To change your icon you need to apply changes to your native iOS project, which was always challenging with Cordova and required some magic to make it work (free frustration included).

However, since Capacitor treats native projects as part of your project’s code which is also checked in to source control, you can easily apply any kind of changes to your native projects to enable native functionalities.

Setting up an Ionic app with Capacitor

If you haven’t used Ionic before, now is the time to set up your environment and install the Ionic CLI.

Now we begin by creating a blank new Ionic application with Capacitor enabled. Once the app is ready we can add our iOS platform and then install the App icon plugin:

ionic start capacitorIcon blank --type=angular --capacitor
cd ./capacitorIcon
ionic capacitor add ios

npm install @capacitor-community/app-icon

Since we need some icons to test this functionality, go ahead and create icons with your favourite image editing tool (I usually use Canva to easily create images) and use the size 1024×1024.

You should have at least two because one of them will be the default icon of our app.

To generate the icons (and optional splashscreens) for your Capacitor application, you can now use the cordova-res package as well. Sounds like a Cordova plugin but works fine for Capacitor!

Now all you need to do is place one of your created icons inside a resources folder at the root of your Ionic app, then install the package globally if you haven’t and run the according command to generate icons for iOS:

# If not yet installed
npm install -g cordova-res

# Create only icons within a Capacitor project
cordova-res ios --skip-config --copy --type icon

We are not interested in creating the splash screen right now so we use the type icon. By skipping the config we tell the tool to not write to a config.xml which is only available in Cordova projects.

Adding icons to your iOS platform

To use alternative icons we need to let our iOS app know about them and include them in the native app bundle.

We can achieve this by first adding a new folder inside Xcode, so go ahead and open it by running:

npx cap open ios

Inside your project, expand the topmost App element, click on the app folder below it and select New Group. I’ve named this group alternative-icons.

Once you’ve done this, you can simply drag your previously created icons onto that folder and select to copy them into it like in the image below.

Add icons to Xcode

You can use the standard 1024×1024 icons here, no need to generate all the different sizes with the Cordova command again. That’s only necessary for your main app icon; these alternative icons just need to be available in the highest resolution and iOS will handle the rest.

Once you have your icons inside Xcode, we also need to reference them inside the Info.plist of your project. You can open that file either from Xcode or your preferred IDE.

Tip: If you want to edit the file directly in Xcode, right click it and select Open as and then Source Code. Otherwise you get the row layout which is good for an overview but makes adding these keys more complicated. This view makes more sense for editing:

Xcode edit plist

Wherever you open it, include a new block inside the dictionary that looks like this:

<dict>
        <key>CFBundleAlternateIcons</key>
        <dict>
            <!-- name which will be used for reference -->
            <key>light</key>
            <dict>
                <key>UIPrerenderedIcon</key>
                <true/>
                <key>CFBundleIconFiles</key>
                <array>
                    <!-- actual file name of the icon -->
                    <string>light</string>
                </array>
            </dict>
            <key>wild</key>
            <dict>
                <key>UIPrerenderedIcon</key>
                <true/>
                <key>CFBundleIconFiles</key>
                <array>
                    <string>wild</string>
                </array>
            </dict>
        </dict>
</dict>

There are two blocks for alternative icons: each has a key by which you will later select the icon, and an array with the actual name of the icon file.

In my case I added two icons called light.png and wild.png to my Xcode project previously, and I’m now able to access them under those keys within the code of our app.

Using the Capacitor plugin

The setup is done and we can focus back on our Ionic app. In fact, there’s not much to do in here besides creating a little UI to display our icons and change them through the plugin.

Let’s start with the TS code. We can perform a few different functions on our plugin in order to show what’s going on:
* getName() will return the name of the selected alternative icon or null
* isSupported() can be called to check if the icon switching functionality is available on your platform
* change() is used to set an alternative icon by its identifier (the one from the plist!)
* reset() will reset our icon to the default initial app icon

Inside our src/app/home/home.page.ts we can now call those functions to keep track of the currently selected icon, and to change the icon to something from our array of icons like this:

import { Component } from '@angular/core';
import { AppIcon } from '@capacitor-community/app-icon';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {
  icons = [
    'wild', 'light'
  ];
  selected = '';

  constructor() {
    this.loadCurrentIcon();
  }

  async loadCurrentIcon() {
    let currentIcon = await AppIcon.getName();
    this.selected = currentIcon.value;
  }

  async setIcon(name: string) {
    const isSupported = await AppIcon.isSupported();

    if (isSupported.value) {
      await AppIcon.change({name: name, suppressNotification: false});
      this.loadCurrentIcon();
    }
  }

  async reset() {
    await AppIcon.reset({suppressNotification: true});
    this.loadCurrentIcon();
  }
}

To display a preview of the icons I also added them to the assets/icon folder of our Ionic app.

Now we can iterate our small array, display a thumbnail for each alternative icon, and call the setIcon() function to change to that icon.

Go ahead and change the src/app/home/home.page.html to this now:

<ion-header>
  <ion-toolbar color="primary">
    <ion-title>
      Capacitor Icons
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content class="ion-padding-top">
  <ion-list>
    <ion-item button *ngFor="let icon of icons" (click)="setIcon(icon)" detail="false">
      <ion-thumbnail slot="start">
        <img [src]="'./assets/icon/' + icon + '.png'">
      </ion-thumbnail>
      <ion-label>
        Select {{ icon }} icon!
      </ion-label>
      <ion-icon name="checkmark" slot="end" *ngIf="selected == icon"></ion-icon>
    </ion-item>
  </ion-list>
  <ion-button expand="full" (click)="reset()" class="ion-margin-top">Reset to original icon</ion-button>
</ion-content>

We can even display which icon is selected since we are able to get the name of the active icon from code!

Now deploy the app to your device by using the new Capacitor run command or use the live reload command to quickly change things on the fly:

# New run command
npx cap run ios

# or use live reload
ionic cap run ios --livereload --external --source-map=false

Important: Make sure that you select your team inside Xcode from the Signing & Capabilities tab to make both of these commands work. Deploying an app to a real device requires signing and an active iOS developer program membership.

Now go ahead and switch icons as you wish and find the perfect icons for your users!

Conclusion

Even the most native piece of functionality is possible to implement given the way Capacitor treats native projects.

You don’t need to rely on copy hooks or any magic to make things like dynamically changing your app icon work. All you need is a good plugin from the Capacitor community and some changes inside the native platforms of your Capacitor project.

And what if there’s no plugin for my needs?

Well, creating your own Capacitor plugin is really easy these days!

PS: I’m also running a full online school for everyone learning Ionic with 60+ video courses, templates and a supportive community – go check out the Ionic Academy and get access to a ton of learning material to boost your Ionic and Capacitor development skills.

PPS: And don’t forget to subscribe to my YouTube channel for fresh Ionic tutorials coming every week!


Simon Grimm