Platform Notes - iOS

Deployment

Developing, building, running, and debugging a Qt for iOS application can all be done with Qt Creator on macOS. The toolchain is provided by Apple's Xcode, and running qmake or CMake on a project targeted for iOS will also generate an Xcode project file (.xcodeproj), with initial application settings. As Qt Creator does not provide an interface for managing all of the settings specific to iOS platform, it is sometimes necessary to adjust them in Xcode directly. Checking that the application is configured correctly is especially important before submitting an application for publishing in Apple's App Store.

Application bundles

iOS applications are typically deployed as self-contained application bundles. The application bundle contains the application's executable as well as dependencies, such as the Qt libraries, plugins, translations, and other resources the application may need.

To build your application as an application bundle with CMake, set the MACOSX_BUNDLE property on your executable target, as follows:

qt_add_executable(app)
if(APPLE)
    set_target_properties(tst_manual_ios_assets PROPERTIES MACOSX_BUNDLE TRUE)
endif()

With qmake, bundles are the default. Set CONFIG -= app_bundle in your project file (.pro) to disable it.

Information Property List Files

Information property list file (Info.plist) on iOS and macOS is used for configuring an application bundle. These configuration settings include:

  • Application display name and identifier
  • Required device capabilities
  • Supported user interface orientations
  • Icons and launch images

See the documentation on Information Property List File in iOS Developer Library for details.

Info.plist with CMake

CMake generates a default Info.plist file if a target has its MACOSX_BUNDLE property set to TRUE. Unfortunately that file is not suitable for iOS projects.

Instead, projects can use qt_add_executable, which will automatically generate an Info.plist file with default values suitable for iOS projects.

To specify a custom Info.plist, projects can set the MACOSX_BUNDLE_INFO_PLIST target property as shown below. Doing that will disable the automatic file generation provided by qt_add_executable and will instead use CMake's native handling for the project-provided Info.plist file.

qt_add_executable(app)
if(IOS)
    set_target_properties(app
        PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/ios/Info.plist")
endif()

See the CMake MACOSX_BUNDLE_INFO_PLIST documentation for information on which target properties and variables can be specified for the template substitution performed by CMake.

Info.plist with QMake

When qmake is run, an Info.plist file is generated with appropriate default values.

It is advisable to replace the generated Info.plist with your own copy, to prevent it from being overwritten the next time qmake is run. You can define a custom information property list with QMAKE_INFO_PLIST variable in your .pro file.

ios {
    QMAKE_INFO_PLIST = ios/Info.plist
}

Application Assets

For files that cannot be bundled into Qt resources, QMAKE_BUNDLE_DATA qmake variable provides a way to specify a set of files to be copied into the application bundle. For example:

ios {
    fontFiles.files = $$files(fonts/*.ttf)
    fontFiles.path = fonts
    QMAKE_BUNDLE_DATA += fontFiles
}

With CMake, the same can be done in the following way:

qt_add_executable(app)
file(GLOB_RECURSE font_files CONFIGURE_DEPENDS "fonts/*.ttf")
if(IOS AND font_files)
    target_sources(app PRIVATE ${font_files})
    set_source_files_properties(
        ${font_files}
        PROPERTIES MACOSX_PACKAGE_LOCATION Resources/fonts)
endif()

For image resources, an alternative way is to make use of asset catalogs in Xcode, which can be added in the following way with qmake:

ios {
    QMAKE_ASSET_CATALOGS += ios/Assets.xcassets
}

With CMake:

qt_add_executable(app)
set(asset_catalog_path "ios/Assets.xcassets")
target_sources(app PRIVATE "${asset_catalog_path}")
set_source_files_properties(
    ${asset_catalog_path}
    PROPERTIES MACOSX_PACKAGE_LOCATION Resources)

Icons

Starting with Xcode 13, icons need to be added to an asset catalog's icon set, which is usually called AppIcon. Xcode will then take care of updating the Info.plist file with the correct keys and values, as well as copy any necessary icon files directly into the application bundle.

Starting with Xcode 14, only one 1024x1024 pixel-sized image is required. Xcode takes care of generating all the necessary icons from it. It is also possible to specify the images manually in the asset catalog.

A detailed list of the icons that can be specified is available at Icon files.

The filename is not important, but the actual pixel size is. To support a universal iOS application, the following images are required:

  • AppIcon60x60@2x.png: 120 x 120 (for iPhone)
  • AppIcon76x76@2x~ipad.png: 152 x 152 (for iPad)
  • AppIcon167x167.png: 167x167 (for iPad Pro)
  • AppIcon1024x1024.png: 1024 x 1024 (for App Store)

Ad-hoc distributions should also include the following filenames in the application bundle to visualize the application in iTunes:

  • iTunesArtwork 512x512
  • iTunesArtwork@2x 1024x1024

The easiest way to add the icons is to follow Xcode's documentation at Create asset catalogs and sets.

When building a project with CMake, it should also specify the following Xcode attribute to ensure app icons are generated by Xcode.

set_target_properties(app_target_name PROPERTIES
    XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME AppIcon)

Below is an example of how an Assets.xcassets/AppIcon.appiconset/Contents.json file for Xcode 14 might look like:

{
  "images" : [
    {
      "idiom" : "universal",
      "platform" : "ios",
      "scale" : "2x",
      "size" : "20x20"
    },
    {
      "idiom" : "universal",
      "platform" : "ios",
      "scale" : "3x",
      "size" : "20x20"
    },
    {
      "idiom" : "universal",
      "platform" : "ios",
      "scale" : "2x",
      "size" : "29x29"
    },
    {
      "idiom" : "universal",
      "platform" : "ios",
      "scale" : "3x",
      "size" : "29x29"
    },
    {
      "idiom" : "universal",
      "platform" : "ios",
      "scale" : "2x",
      "size" : "38x38"
    },
    {
      "idiom" : "universal",
      "platform" : "ios",
      "scale" : "3x",
      "size" : "38x38"
    },
    {
      "idiom" : "universal",
      "platform" : "ios",
      "scale" : "2x",
      "size" : "40x40"
    },
    {
      "idiom" : "universal",
      "platform" : "ios",
      "scale" : "3x",
      "size" : "40x40"
    },
    {
      "idiom" : "universal",
      "platform" : "ios",
      "scale" : "2x",
      "size" : "60x60"
    },
    {
      "idiom" : "universal",
      "platform" : "ios",
      "scale" : "3x",
      "size" : "60x60"
    },
    {
      "idiom" : "universal",
      "platform" : "ios",
      "scale" : "2x",
      "size" : "64x64"
    },
    {
      "idiom" : "universal",
      "platform" : "ios",
      "scale" : "3x",
      "size" : "64x64"
    },
    {
      "idiom" : "universal",
      "platform" : "ios",
      "scale" : "2x",
      "size" : "68x68"
    },
    {
      "idiom" : "universal",
      "platform" : "ios",
      "scale" : "2x",
      "size" : "76x76"
    },
    {
      "idiom" : "universal",
      "platform" : "ios",
      "scale" : "2x",
      "size" : "83.5x83.5"
    },
    {
      "filename" : "AppIcon1024x1024.png",
      "idiom" : "universal",
      "platform" : "ios",
      "size" : "1024x1024"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}

Launch Screens and Launch Images

Launch Screens

Every iOS app must provide a launch screen to be displayed while the app launches. A launch screen is an interface builder .xib file, also called a storyboard file. For more information, see Specifying Your App's Launch Screen.

Support for launch screens has been introduced in iOS 9.0.

Both qmake and CMake generate a default launch screen called LaunchScreen.storyboard.

To specify a custom launch screen, it must be copied to the application bundle, and the UILaunchStoryboardName key must be set to the name of the launch screen in the Info.plist file.

Qt supports custom launch screens with CMake since Qt 6.4, and with qmake since Qt 6.0.

Assuming that the launch file is called Launch.storyboard, it can be added to Info.plist as follows:

<key>UILaunchStoryboardName</key>
<string>Launch</string>

To copy the launch screen into the application bundle with qmake, use the following code snippet in your project .pro file:

ios {
    QMAKE_IOS_LAUNCH_SCREEN = $$PWD/Launch.storyboard
}

With CMake:

qt_add_executable(app)
if(IOS)
    set_target_properties(app PROPERTIES
        QT_IOS_LAUNCH_SCREEN "${CMAKE_CURRENT_SOURCE_DIR}/Launch.storyboard")
endif()

Launch Images

It's also possible to specify launch images (PNG files) instead of launch screen.

Note: Using launch images is not recommended, because support for them has been deprecated since iOS 13.0. Consider switching to launch screens instead.

Launch images must be copied to the application bundle and their names must be set in the Info.plist file using the UILaunchImages key.

The following images must be prepared:

  • LaunchImage-iOS7-568h@2x.png: 640 x 1136
  • LaunchImage-iOS7-Landscape.png: 1024 x 768
  • LaunchImage-iOS7-Landscape@2x.png: 2048 x 1536
  • LaunchImage-iOS7-Portrait.png: 768 x 1024
  • LaunchImage-iOS7-Portrait@2x.png: 1536 x 2048
  • LaunchImage-iOS7@2x.png: 640 x 960

The images can be added to Info.plist as follows:

<key>UILaunchImages</key>
<array>
    <dict>
        <key>UILaunchImageMinimumOSVersion</key>
        <string>7.0</string>
        <key>UILaunchImageName</key>
        <string>LaunchImage-iOS7</string>
        <key>UILaunchImageOrientation</key>
        <string>Portrait</string>
        <key>UILaunchImageSize</key>
        <string>{320, 568}</string>
    </dict>
    <dict>
        <key>UILaunchImageMinimumOSVersion</key>
        <string>7.0</string>
        <key>UILaunchImageName</key>
        <string>LaunchImage-iOS7</string>
        <key>UILaunchImageOrientation</key>
        <string>Portrait</string>
        <key>UILaunchImageSize</key>
        <string>{320, 480}</string>
    </dict>
</array>
<key>UILaunchImages~ipad</key>
<array>
    <dict>
        <key>UILaunchImageMinimumOSVersion</key>
        <string>7.0</string>
        <key>UILaunchImageName</key>
        <string>LaunchImage-iOS7-Landscape</string>
        <key>UILaunchImageOrientation</key>
        <string>Landscape</string>
        <key>UILaunchImageSize</key>
        <string>{768, 1024}</string>
    </dict>
    <dict>
        <key>UILaunchImageMinimumOSVersion</key>
        <string>7.0</string>
        <key>UILaunchImageName</key>
        <string>LaunchImage-iOS7-Portrait</string>
        <key>UILaunchImageOrientation</key>
        <string>Portrait</string>
        <key>UILaunchImageSize</key>
        <string>{768, 1024}</string>
    </dict>
    <dict>
        <key>UILaunchImageMinimumOSVersion</key>
        <string>7.0</string>
        <key>UILaunchImageName</key>
        <string>LaunchImage-iOS7</string>
        <key>UILaunchImageOrientation</key>
        <string>Portrait</string>
        <key>UILaunchImageSize</key>
        <string>{320, 568}</string>
    </dict>
    <dict>
        <key>UILaunchImageMinimumOSVersion</key>
        <string>7.0</string>
        <key>UILaunchImageName</key>
        <string>LaunchImage-iOS7</string>
        <key>UILaunchImageOrientation</key>
        <string>Portrait</string>
        <key>UILaunchImageSize</key>
        <string>{320, 480}</string>
    </dict>
</array>

To copy the launch images into the application bundle with qmake, use the following code snippet in your project .pro file:

ios {
    app_launch_images.files = $$files($$PWD/ios/LaunchImage*.png)
    QMAKE_BUNDLE_DATA += app_launch_images
}

With CMake:

qt_add_executable(app)
file(GLOB_RECURSE launch_images CONFIGURE_DEPENDS "ios/LaunchImage*.png")
if(IOS AND launch_images)
    target_sources(app PRIVATE ${launch_images})
    set_source_files_properties(
        ${launch_images}
        PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
endif()

Note: Earlier iOS versions supported specifying a single launch image using the UILaunchImageFile key in Info.plist, but support for it has been deprecated since iOS 10.0.

Native Image Picker

If your Info.plist file contains an entry for NSPhotoLibraryUsageDescription, qmake will automatically include an extra plugin which enables access to the native image picker.

For CMake, manually link the native image picker with the qt_import_plugins:

qt_import_plugins(app INCLUDE Qt6::QIosOptionalPlugin_NSPhotoLibraryPlugin)

If the directory in your QFileDialog is set to:

QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).last();

or alternatively the folder in a FileDialog in QML to:

shortcuts.pictures

then the native image picker is shown to allow access to the user's photo album.

Expressing Supported iOS Versions

Apple platforms have a built-in way to express the OS versions that an application supports, which allows older versions of the platforms to automatically display a user friendly error message prompting the user to update their OS, as opposed to crashing and displaying a stack trace.

The main concepts involved in expressing support for a particular range of OS versions are:

  • Deployment target specifies the hard minimum version of macOS or iOS that your application supports.
  • SDK version specifies the soft maximum version of macOS or iOS that your application supports.

When you develop an application for an Apple platform, you should always use the latest version of Xcode and the latest SDK available at the time of development. On some platforms, like iOS, you will actually be rejected from the App Store if you do not. Therefore, the SDK version is always greater than or equal to the deployment target.

When you develop an application for an Apple platform, you must set the deployment target. Various build tools within the Xcode toolchain all have a flag which you can use to set this value, including but not limited to the compiler and linker. By setting the deployment target value, you are explicitly declaring that your application must work on at least that version, and will not work with any earlier versions of the OS. It is then up to you to ensure that your use of the system APIs matches what you have declared. Since the compiler knows what you have declared, it can help in enforcing that.

The SDK version is considered a soft maximum version of the OS that an application is compatible with in the way that if the application is built with an SDK, it will continue to use the behaviors of that SDK even on newer OS versions, because the OS checks the binary's load commands and emulates backwards compatibility with the older OS. For example, if an application is built with the macOS 10.12 SDK, it will continue to use 10.12 behaviors even on 10.13 and above.

However, Mach-O binaries are inherently forward compatible. For example, an application built with the iOS 9 SDK will run just fine on iOS 10, but might not be opted into whatever behavior changes may have been made to certain functionality on the new release, until that application is recompiled against that newer SDK.

The minimum OS version can be expressed to the system by the compiler and linker flags that embed it into the Mach-O binary. In addition, the LSMinimumSystemVersion key must be set in the application's app bundle. This value must be equal to the value passed to the compiler and linker, because on macOS it will allow the OS to display a user friendly error dialog that says the application requires a newer version of the OS as opposed to a crash dialog. The LSMinimumSystemVersion is also the key that the App Store uses to display the required OS version; the compiler and linker flags have no power there.

For the most part, Qt applications will work without problems. For example, in qmake, the Qt mkspecs set QMAKE_IOS_DEPLOYMENT_TARGET or QMAKE_MACOSX_DEPLOYMENT_TARGET to the minimum version that Qt itself supports. Similarly, in Qbs, the Qt modules set cpp.minimumIosVersion, cpp.minimumMacosVersion, cpp.minimumTvosVersion, or cpp.minimumWatchosVersion to the minimum version that Qt itself supports.

However, you must take care when you manually set your own target version. If you set it to a value higher than what Qt requires and supply your own Info.plist file, you must add an LSMinimumSystemVersion entry to the Info.plist that matches the value of the deployment target, because the OS will use the LSMinimumSystemVersion value as the authoritative one.

If you specify a deployment target value lower than what Qt requires, the application will almost certainly crash somewhere in the Qt libraries when run on an older version than Qt supports. Therefore, make sure that the actual build system code reflects the minimum OS version that is actually required.

Publishing to Apple App Store

Verifying that your Qt for iOS application is ready for publishing to App Store can be done as described in Submitting the Application. To submit the application, you can use Xcode, or the Application Loader (installed with Xcode). Qt Creator does not provide an interface for managing all of the settings in an Xcode project configuration.

The application should be tested on the iOS versions and devices that it is targeted to support. The minimum deployment target for Qt applications varies by Qt version. For more information, see supported configurations.

The actual publishing process involves creating a distribution certificate and a provision profile, creating a signed archive of your application, and running a set of validation tests on it.

See the App Distribution Guide in iOS Developer Library for more information.

Symbol visibility warnings

In the context of linking C++ libraries, functions and objects are referred to as symbols. Symbols can have either default or hidden visibility.

For performance reasons, Qt and many other libraries compile their sources using hidden visibility by default, and only mark symbols with default visibility when they are meant to be used in user projects.

Unfortunately the Apple linker can issue warnings when one library is compiled with hidden visibility and a user project application or library is compiled with default visibility.

If project developers want to silence the warning, they need to build their project code with hidden visibility as well.

In CMake that can be done by adding the following code to the your CMakeLists.txt:

set(CMAKE_CXX_VISIBILITY_PRESET hidden)

In qmake that can be done by adding the following code to your .pro file:

CONFIG+=hide_symbols

In case if a project builds libraries, any symbols in the library that are meant to be used in another library or application will have to be explicitly marked with default visibility. For example, that can be done by annotating such functions or classes with Q_DECL_EXPORT.

Product archiving issue with CMake

Due to an issue in CMake, trying to create a product archive with an iOS application may fail.

This can happen both when trying to create the archive in Xcode using the Product -> Archive menu item, or from the command line using xcodebuild -archivePath.

The error message might reference undefined symbols or non-existent file paths.

To work around the issue, make sure to build a Release version of the project before trying to create an archive.

dSYM bundle missing in xcarchive created by a CMake Xcode project

Due to a bug in Xcode and certain CMake limitations, a CMake-generated Xcode project will fail to include an application's dSYM bundle into an xcarchive, during Xcode's archiving task.

Qt provides a workaround as an opt-in, so that the dSYM bundle is included in the xcarchive, but it comes with trade-offs. That is, the following CMake features will not work correctly:

  • any $<TARGET_FILE:app> generator expressions might expand to an invalid path that does not lead to the app binary
  • the CMAKE_RUNTIME_OUTPUT_DIRECTORY variable and its associated RUNTIME_OUTPUT_DIRECTORY target property will be ignored even if set
  • other unknown issues

To mitigate the issues above, you can:

  • only enable the workaround when you intend to create the xcarchive and not during project development
  • make sure you only add executables and libraries in the root project directory, and not in add_subdirectory calls.

To enable the workaround, configure the project with the following option:

cmake . -DQT_USE_RISKY_DSYM_ARCHIVING_WORKAROUND=ON

or set the variable in the project before any qt_add_executable or qt_add_library calls:

set(QT_USE_RISKY_DSYM_ARCHIVING_WORKAROUND ON)

...

qt_add_executable(app)

© 2024 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.