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.

@paul.masristone , @craig.scott , @camgaertner can I please ask for a quick help here?

Everything you have used above works fine for me, no issue with the build in QT (i didnt het futher yet),
the only problem i have is in CMake on this line:
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/ios/Info.plist.in"
of course its inside set_target_properties()

this gives me just error:

The following build commands failed:
	ProcessInfoPlistFile /Users/z659406/Desktop/Qt\ Builds/Release-iphoneos/appfilmtoro.app/Info.plist /Users/z659406/Desktop/Qt\ Builds/CMakeFiles/appfilmtoro.dir/Info.plist (in target 'appfilmtoro' from project 'filmtoro')
(1 failure)
13:11:10: The process "/Users/z659406/Qt/Tools/CMake/CMake.app/Contents/bin/cmake" exited with code 65.
Error while building/deploying project filmtoro (kit: Qt 6.6.2 for iOS)
When executing step "Build"

the Info.plist.in looks like this:

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

full CMakeLists attached if that helps
CMakeLists.txt (3.8 KB)

Thereā€™s a lot I learned along the way, with help from Craig and others and I have since successfully published on the App Store.

Iā€™ve distilled this knowledge into the following Gist: CMake for Qt 6 + Xcode (target iOS) Ā· GitHub

Youā€™ll see that, as part of this, you only need to include the asset catalog; you donā€™t need to (and shouldnā€™t) include the individual files anymore.

I hope a look at both the CMake and the Info.plist.in will answer your questions.

thank you @paul.masristone
it worksā€¦ however i had one error during build (archive) in Xcode, which afterall I handledā€¦ but I think its caused by CMake from QT build.

Xcode gave me error ā€œArchive missing bundle identifierā€ā€¦
I was checking everything, and finally i found that in Xcode in Project Info all data are empty:


so i did fill them manualy and it let me Archive finallyā€¦

shouldnt this pass from build in QT to Info.plist so Xcode recognizes and uses that?

rightā€¦ so replace yours @MACOSX_BUNDLE_BUNDLE_NAME@ by ${MACOSX_BUNDLE_BUNDLE_NAME} and all works fine :slight_smile:
thank you for the helpā€¦ very awesome :slight_smile:

maybe if I can ask also hereā€¦ can you guide me, show me example, gimme any tips for the splash screen?

Both @...@ and ${...} get replaced when CMake configures a file, so I donā€™t know why youā€™ve needed to change them ā€” they work for me as in the gist ā€” but if youā€™ve got something working, all good! :+1:

For the launch screen, my approach was to create something very simple in Xcode and then put the .storyboard file it created into my repo. In terms of guidance, Iā€™m not sure. Iā€™d say:

  • Apple have guidance on this, and what theyā€™d find acceptable (though most game apps ignore that advice).
  • Other than the very first time the app fires up, when it appears for a couple of seconds, in my experience, you pretty much never see it, so donā€™t sweat it.
  • If you want a splash screen that stays for a couple of seconds, then youā€™ll need to build that into your app.

Hey, what about using - seems to work well :slight_smile:

set_target_properties(MyApp PROPERTIES XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME ā€œAppIconā€)

hello guys,

I need to ask again, because I cant figure this out :frowning: I on iOS 18 beta (but I dont think it has any difference then other iOS versions).

so, I have structure of my project folder:

project-root
|- ios
   |- Info.plist.in
   |- MyAppIcons.xcassets
      |- Contents.json
      |- AppIcon.appiconset
         |- Contents.json
         |- AppIcon@2x
         |- AppIcon@2x~ipad
         |- AppIcon@3x
         |- AppIcon~ios-marketing
         |- AppIcon~ipad
         |- AppIcon-20@2x
         |- AppIcon-20@2x~ipad
         |- AppIcon-20@3x
         |- AppIcon-20~ipad
         |- AppIcon-29
         |- AppIcon-29@2x
         |- AppIcon-29@2x~ipad
         |- AppIcon-29@3x
         |- AppIcon-29~ipad
         |- AppIcon-40@2x
         |- AppIcon-40@2x~ipad
         |- AppIcon-40@3x
         |- AppIcon-40~ipad
         |- AppIcon-60@2x~car
         |- AppIcon-60@3x~car
         |- AppIcon-83.5@2x~ipad

the ios/MyAppIcons.xcassets/AppIcon.appiconset/Contents.json contains:

{
  "images": [
    {
      "filename": "AppIcon@2x.png",
      "idiom": "iphone",
      "scale": "2x",
      "size": "60x60"
    },
    {
      "filename": "AppIcon@3x.png",
      "idiom": "iphone",
      "scale": "3x",
      "size": "60x60"
    },
    {
      "filename": "AppIcon~ipad.png",
      "idiom": "ipad",
      "scale": "1x",
      "size": "76x76"
    },
    {
      "filename": "AppIcon@2x~ipad.png",
      "idiom": "ipad",
      "scale": "2x",
      "size": "76x76"
    },
    {
      "filename": "AppIcon-83.5@2x~ipad.png",
      "idiom": "ipad",
      "scale": "2x",
      "size": "83.5x83.5"
    },
    {
      "filename": "AppIcon-40@2x.png",
      "idiom": "iphone",
      "scale": "2x",
      "size": "40x40"
    },
    {
      "filename": "AppIcon-40@3x.png",
      "idiom": "iphone",
      "scale": "3x",
      "size": "40x40"
    },
    {
      "filename": "AppIcon-40~ipad.png",
      "idiom": "ipad",
      "scale": "1x",
      "size": "40x40"
    },
    {
      "filename": "AppIcon-40@2x~ipad.png",
      "idiom": "ipad",
      "scale": "2x",
      "size": "40x40"
    },
    {
      "filename": "AppIcon-20@2x.png",
      "idiom": "iphone",
      "scale": "2x",
      "size": "20x20"
    },
    {
      "filename": "AppIcon-20@3x.png",
      "idiom": "iphone",
      "scale": "3x",
      "size": "20x20"
    },
    {
      "filename": "AppIcon-20~ipad.png",
      "idiom": "ipad",
      "scale": "1x",
      "size": "20x20"
    },
    {
      "filename": "AppIcon-20@2x~ipad.png",
      "idiom": "ipad",
      "scale": "2x",
      "size": "20x20"
    },
    {
      "filename": "AppIcon-29.png",
      "idiom": "iphone",
      "scale": "1x",
      "size": "29x29"
    },
    {
      "filename": "AppIcon-29@2x.png",
      "idiom": "iphone",
      "scale": "2x",
      "size": "29x29"
    },
    {
      "filename": "AppIcon-29@3x.png",
      "idiom": "iphone",
      "scale": "3x",
      "size": "29x29"
    },
    {
      "filename": "AppIcon-29~ipad.png",
      "idiom": "ipad",
      "scale": "1x",
      "size": "29x29"
    },
    {
      "filename": "AppIcon-29@2x~ipad.png",
      "idiom": "ipad",
      "scale": "2x",
      "size": "29x29"
    },
    {
      "filename": "AppIcon-60@2x~car.png",
      "idiom": "car",
      "scale": "2x",
      "size": "60x60"
    },
    {
      "filename": "AppIcon-60@3x~car.png",
      "idiom": "car",
      "scale": "3x",
      "size": "60x60"
    },
    {
      "filename": "AppIcon~ios-marketing.png",
      "idiom": "ios-marketing",
      "scale": "1x",
      "size": "1024x1024"
    }
  ],
  "info": {
    "author": "iconkitchen",
    "version": 1
  }
}

then, in my Info.plist.in I have:

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

and in my CMakeLists.txt:

if (CMAKE_SYSTEM_NAME STREQUAL "iOS")

	# Asset catalog root
    set(asset_catalog_path ${CMAKE_CURRENT_SOURCE_DIR}/ios/MyAppIcons.xcassets)
	target_sources(${TARGET_NAME} PRIVATE ${asset_catalog_path})
	set_source_files_properties(${asset_catalog_path} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)

    set_target_properties(${TARGET_NAME} PROPERTIES
	    MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/ios/Info.plist.in"
		XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME "AppIcon"
    )
	
endif()

but when deployed, i dont see the application icon unfortunatellyā€¦ however as soon as I click on the application to start, i see the animation how my real icon gets bigger over the all screen, just before the launchscreen startsā€¦ and same when i hit home button to minimize the applicationā€¦ i see nic eanimation where my icon smaller and moved to the place where the app icon is on desktopā€¦ however as soon as its get to th eplace, the ā€œradarā€ icon shows up instead
so it knows the images, only does not use them as application iconā€¦

what am I missing here? is it possible i miss some needed format? or when i only ā€œdebugā€ the app so its not released, then icons are not used?

I havenā€™t tried iOS 18 yet, but did you follow all the steps in my earlier response? How to incorporate app icon files for iOS into Xcode 14? - #10 by paul.masristone

Previously (Feb 27) you said youā€™d got as far as building in Qt but hadnā€™t tried the next steps, so maybe you hadnā€™t fully resolved things before??

it fixed itself at the end.
I was opening minimizing the app to see the animation how icon from big get ssmaller and animation moves to the place where it sits on the desktop, then it always turned in to the original nonIcon picture (radar type?)ā€¦ i did this like 30times and once the icon stayed there on the desktop and its there since :smiley: so I guess its some sort of bug, or the icon needs some approval in order for people not to put any unwanted content, or i dont know why

UPDATE: Iā€™m coming back to a previous part of this thread as Iā€™ve made some updates that simplify things.

Previously I said

This was true at the time, but it looks like Qt made some changes and itā€™s no longer true. This explains why @shokarta had problems with the @...@.

Looking at Qtā€™s default Info.plist in Qt 6.5+ I see that they have a number of ${...}, one $(...) and one @...@ for the storyboard. I suspect that they now do things in stages such that the @...@ get evaluated first, for the storyboard before the other properties have been set up, and then the ${...} get evaluated later on once the properties have been set up.

In any case, I have now made some more radical changes, which you can see in my updated Gist:

  • Other than Qtā€™s storyboard @qt_ios_launch_screen_plist_entry@, I have removed all ${...} and replaced them with Xcode attributes of the form $(...).
  • In CMake I set up the corresponding XCODE_ATTRIBUTE_XXXX.
  • I have also cleared out incorrect keys from the Info.plist (macOS keys and deprecated keys)
  • @stf888 was right about the property XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME and I discovered this was sufficient on its own, so Iā€™ve stripped out icon-related keys from my Info.plist.