How to incorporate app icon files for iOS into Xcode 14?

I have previously published my Qt app using Qt 5 + qmake.
I am now ready to release a new version using Qt 6 + CMake. (I am using Qt 6.4.2 and CMake 3.24.2)

However I am struggling to get app icons into Xcode 14.2 in a form that it will accept. Here are a couple of close examples.

In both cases, I have the following file structure for the app icons:

project-root
|- Images.xcassets
   |- Contents.json
   |- AppIcon.appiconset
      |- Contents.json
      |- AppIcon~iOS-Marketing-1024@1x.png
      |- AppIcon~iPad-20@1x.png
      |- AppIcon~iPad-20@2x.png
      |- AppIcon~iPad-29@1x.png
      |- AppIcon~iPad-29@2x.png
      ...
      |- AppIcon~iPhone-60@2x.png
      |- AppIcon~iPhone-60@3x.png

and both examples use the following CMake code

set_target_properties(MyApp PROPERTIES
    MACOSX_BUNDLE TRUE
    MACOSX_BUNDLE_GUI_IDENTIFIER org.myorg.myapp
    MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
    MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
    MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in"
    QT_IOS_LAUNCH_SCREEN "${CMAKE_CURRENT_SOURCE_DIR}/LaunchScreen.storyboard"
)

... code to include icons goes here ...

target_link_libraries(MyApp
    PRIVATE Qt6::Quick)

install(TARGETS MyApp
    BUNDLE DESTINATION .
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})

In both examples, the app runs fine on my iPad and it can be archived, but the archive fails validation with the same errors:

Example 1
Here I include the icon image files directly (equivalent to how I did it with Qt 5 and Xcode 11(?)).

CMakeLists.txt

file(GLOB app_icon_files CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/Images.xcassets/AppIcon.appiconset/*.png")
if (app_icon_files)
    target_sources(MyApp PRIVATE ${app_icon_files})
    set_source_files_properties(${app_icon_files} PROPERTIES
        MACOSX_PACKAGE_LOCATION Resources
    )
else()
    message(WARNING "iOS app icon files not found")
endif()

Info.plist.in

    <key>CFBundleIconName</key>
    <string>AppIcon</string>
    <key>CFBundleIcons</key>
    <dict>
        <key>CFBundlePrimaryIcon</key>
        <dict>
            <key>CFBundleIconFiles</key>
            <array>
            <string>AppIcon~iPhone-20.png</string>
            <string>AppIcon~iPhone-29.png</string>
            <string>AppIcon~iPhone-40.png</string>
            <string>AppIcon~iPhone-57.png</string>
            <string>AppIcon~iPhone-60.png</string>
            <string>AppIcon~iPad-20.png</string>
            <string>AppIcon~iPad-29.png</string>
            <string>AppIcon~iPad-40.png</string>
            <string>AppIcon~iPad-76.png</string>
            <string>AppIcon~iPad-83.5.png</string>
            <string>AppIcon~iOS-Marketing-1024.png</string>
            </array>
        </dict>
    </dict>

Results

  • I can see the icon images individually listed in the project Resources folder.
  • Xcode doesn’t show the icon against the project or target.
  • However the app icon correctly appears on my iPad when I do a build and run in Xcode.
  • When I build an archive and run ‘Validate app’, the icon shows up on the validation popup on the “Review MyApp.ipa content” screen just before clicking ‘Validate’ even though it didn’t show up against the archive in the list of archives.

Example 2
Here I include the image files as an asset catalog. I used the CMake code snippet below suggested by @craig.scott that he acknowledges is not officially supported by CMake to achieve this.

CMakeLists.txt

target_sources(MyApp PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/Images.xcassets")
set_source_files_properties("${CMAKE_CURRENT_SOURCE_DIR}/Images.xcassets"
    PROPERTIES MACOSX_PACKAGE_LOCATION Resources
)

Info.plist.in

...
    <key>CFBundleIconName</key>
    <string>Images</string>
    <key>CFBundleIcons</key>
    <dict>
        <key>CFBundlePrimaryIcon</key>
        <dict>
            <key>CFBundleIconFiles</key>
            <array>
            	<string>Images</string>
            </array>
        </dict>
    </dict>
...

Results

  • I can see the Images asset catalog in the project Resources folder. Clicking it shows the name AppIcon with the correct icon and a sheet of the icon at all different sizes, as it should be.
  • Xcode doesn’t show the icon against the project or target.
  • The app icon does not appear on my iPad when I do a build and run in Xcode.
  • The icon does not appear anywhere in the archive or validation steps.

NB: changing the value for CFBundleIconName to “AppIcon” or removing this key entirely makes no difference.

It’s been a very long journey on this but I’ve finally got the answer.

Step 1: Create your app icon set.

My way of doing this has been to go into Xcode, go File > New > Asset Catalog. Put it all together with all the icons. Then go find this in Finder and copy the whole asset catalog folder into my project repo. This contains the right structure and also saves you writing the Content.json files.

Note that as of Xcode 14, for iOS you can have a single 1024x1024 image if you’re happy for Xcode to do all the resizing. See online for how to do this. This still generates a structure that can be used as before.

Let’s say your folder structure is: MyAppIcons.xcasset containing AppIcon.appiconset. This means your asset catalog is called MyAppIcons and your app icon set is called AppIcon. This distinction is crucial for getting this to work right.

Step 2: Put the asset catalog name into Info.plist.in

...
    <key>CFBundleIcons</key>
    <dict>
        <key>CFBundlePrimaryIcon</key>
        <dict>
            <key>CFBundleIconFiles</key>
            <array>
            <string>MyAppIcons</string>
            </array>
        </dict>
    </dict>
...

Note the name of the asset catalog goes in there as the only array element and you don’t include the file extension.

You also don’t need to have the CFBundleIconName key (despite the wording of the validation error I was getting). Xcode gets everything it needs from the asset catalog instead.

Step 3: In CMake, copy the asset catalog into the Resources folder

Unlike what @craig.scott suggested, it’s not sufficient to just include the asset catalog folder. You have to include all the folders and files in the structure and you have to preserve the folder structure.

# Asset catalog root
target_sources(MyApp PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/MyAppIcons.xcassets")
set_source_files_properties("${CMAKE_CURRENT_SOURCE_DIR}/MyAppIcons.xcassets" PROPERTIES
    MACOSX_PACKAGE_LOCATION Resources
)

# Asset catalog app icon set
list(APPEND app_icon_set "${CMAKE_CURRENT_SOURCE_DIR}/MyAppIcons.xcassets/AppIcon.appiconset")
list(APPEND app_icon_set "${CMAKE_CURRENT_SOURCE_DIR}/MyAppIcons.xcassets/Contents.json")
set_source_files_properties(${app_icon_set} PROPERTIES
    MACOSX_PACKAGE_LOCATION Resources/MyAppIcons.xcassets
)

# Asset catalog icon files
file(GLOB app_icon_files CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/MyAppIcons.xcassets/AppIcon.appiconset/*.png")
list(APPEND app_icon_files "${CMAKE_CURRENT_SOURCE_DIR}/MyAppIcons.xcassets/AppIcon.appiconset/Contents.json")
set_source_files_properties(${app_icon_set} PROPERTIES
    MACOSX_PACKAGE_LOCATION Resources/MyAppIcons.xcassets/AppIcon.appiconset
)

Step 4: Manually set the App Icon field

Run CMake to generate the Xcode project and open that in Xcode.

You can verify the following:

  • In your folder structure you’ll see Root folder > MyApp > Resources > MyAppIcons. Click this and you’ll see the AppIcon icon set.
  • In your folder structure you’ll see Root folder > MyApp > Resources > Info. Click this and you’ll see the contents of your generated Info.plist. You should see Icon files (iOS 5) > Primary icon > Icon files > Item 0: MyAppIcons

Now you have to do a manual step in Xcode that unfortunately CMake can’t do AFAIK:

  • Select the project root folder.
  • In the main panel, select your MyApp target. It’ll have a generic app icon to its left.
  • Select the General tab
  • Under App Icons and Launch Screen, set App Icon to AppIcon. This is what tells Xcode the name of your app icon within the asset catalog you’ve already identified in the Info.plist.

You should now find that if you build and run the app on a device, the app icon is present.

Step 5: Create a release build before you archive (if using CMake < 3.25)

There’s an issue with CMake 3.24 and below that means archives don’t get generated properly. Here’s the workaround:

  • In Xcode, go to Product > Scheme > Edit Scheme…
  • Select MyApp target (top left item of main white panel, even though it doesn’t look selectable)
  • Select Run on the left
  • Change Build Configuration to Release. (I uncheck Debug executable too but I don’t know if this matters.)
  • While you’re there confirm that the left pane now says Run (Release) and Archive (Release)
  • Close the panel
  • Go to Product > Build For > Running. This generates the necessary files both for running and for archive.

Now you can go Product > Archive and, assuming you’re code signing is properly set up, you should be able to Validate app successfully.

Phew! :sweat_smile:

After investigating this further, I found it was not necessary to list out all the individual contents of the Assets.xcassets directory or perform any manual actions. The key piece of missing information was that macOS apps appear to need to link to Cocoa or AppKit, otherwise the automatic compiling of asset catalogs and storyboards doesn’t work. There are also various build settings related to Info.plist handling which can be set to give a project with the expected settings. See discussions in issue 24399, in particular this comment where I posted a reasonably complete macOS example that works for me with Xcode 14 (you should be able to adapt it for iOS).

Thanks @craig.scott, that’s really timely.
I’m about to publish a new version of my client’s app (in the next week), so I’ll test this.

It’s an interesting departure from the previous method centred around the Info.plist. If I notice any differences for iOS, I’ll add them to the issue and link from here.

I was able to get an iOS app to work with only a few changes to my macOS example, so the general approach laid out there seems to work. Even managed to run it on my mac as a “Mac (Designed for iPad)” app.

1 Like

@craig.scott Related to this new approach (but not related to incorporating icon files), do you have any ideas about incorporating fonts? In my case I use a custom font in the launch screen. Maybe you could respond on this thread? Add custom font to Xcode (for LaunchScreen)

Sorry, I have no experience in working with custom fonts, and to my knowledge it hasn’t come up before, so I can’t point you to any prior discussions.

Paul, this method works quite well. Thanks for sharing. I found a small improvement that may be useful to you.

Now you have to do a manual step in Xcode that unfortunately CMake can’t do AFAIK:

From this project, it’s possible to modify XCode project files via CMake.

I tested it and it seems to work. If you don’t want to include the entire CMake file, you can also copy this code.

macro(qt_ios_set_xcode_property TARGET XCODE_PROPERTY XCODE_VALUE XCODE_RELVERSION)
  set(XCODE_RELVERSION_I "${XCODE_RELVERSION}")
  if(XCODE_RELVERSION_I STREQUAL "All")
    set_property(TARGET ${TARGET} PROPERTY XCODE_ATTRIBUTE_${XCODE_PROPERTY} "${XCODE_VALUE}")
  else()
    set_property(TARGET ${TARGET} PROPERTY XCODE_ATTRIBUTE_${XCODE_PROPERTY}[variant=${XCODE_RELVERSION_I}] "${XCODE_VALUE}")
  endif()
endmacro() # qt_ios_set_xcode_property

# Now call it on your target for the app icon property.
qt_ios_set_xcode_property (${MY_TARGET} ASSETCATALOG_COMPILER_APPICON_NAME "MyAppIcons" "All")

In your generated xcodeproj file, you can validate that this was set within the project.pbxproj file.