Platform Notes - iOS | Qt 6.4

- Qt 6.4
- Platform Notes - iOS
- Qt 6.4.1 Reference Documentation

# 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

Icons need to be set in the `Info.plist` file and then copied to the application bundle. Xcode has special support for icons, but when using Qt, it is usually better to set them in the .pro file or the CMakeLists.txt file.

To support all resolutions and devices, several images should be created. A detailed list of what is required is available at Icon files. The filename is not important, but the actual pixel size is. Just a few icons are required. However, to support both iPhone and iPad, the following images are required:

- AppIcon29x29.png: 29 x 29
- AppIcon29x29@2x.png: 58 x 58
- AppIcon29x29@2x~ipad.png: 58 x 58
- AppIcon29x29~ipad.png: 29 x 29
- AppIcon40x40@2x.png: 80 x 80
- AppIcon40x40@2x~ipad.png: 80 x 80
- AppIcon40x40~ipad.png: 40 x 40
- AppIcon50x50@2x~ipad.png: 100 x 100
- AppIcon50x50~ipad.png: 50 x 50
- AppIcon57x57.png: 57 x 57
- AppIcon57x57@2x.png: 114 x 114
- AppIcon60x60@2x.png: 120 x 120
- AppIcon72x72@2x~ipad.png: 144 x 144
- AppIcon72x72~ipad.png: 72 x 72
- AppIcon76x76@2x~ipad.png: 152 x 152
- AppIcon76x76~ipad.png: 76 x 76

These files should be copied to the application bundle by adding the following code snippet to the project .pro file:

     ios {
         ios_icon.files = $$files($$PWD/ios/AppIcon*.png)
         QMAKE_BUNDLE_DATA += ios_icon
     }

With CMake:

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

For the icons to be used, the filenames must also be listed in the `Info.plist` file. The best way is to list all icon files using the `CFBundleIconFiles` key. The iPad specific version can be given using the `CFBundleIcons~ipad` key, by adding the following code snippet to `Info.plist`:

     <key>CFBundleIcons</key>
     <dict>
         <key>CFBundlePrimaryIcon</key>
         <dict>
             <key>CFBundleIconFiles</key>
             <array>
                 <string>AppIcon29x29.png</string>
                 <string>AppIcon29x29@2x.png</string>
                 <string>AppIcon40x40@2x.png</string>
                 <string>AppIcon57x57.png</string>
                 <string>AppIcon57x57@2x.png</string>
                 <string>AppIcon60x60@2x.png</string>
             </array>
         </dict>
     </dict>
     <key>CFBundleIcons~ipad</key>
     <dict>
         <key>CFBundlePrimaryIcon</key>
         <dict>
             <key>CFBundleIconFiles</key>
             <array>
                 <string>AppIcon29x29.png</string>
                 <string>AppIcon29x29@2x.png</string>
                 <string>AppIcon40x40@2x.png</string>
                 <string>AppIcon57x57.png</string>
                 <string>AppIcon57x57@2x.png</string>
                 <string>AppIcon60x60@2x.png</string>
                 <string>AppIcon29x29~ipad.png</string>
                 <string>AppIcon29x29@2x~ipad.png</string>
                 <string>AppIcon40x40~ipad.png</string>
                 <string>AppIcon40x40@2x~ipad.png</string>
                 <string>AppIcon50x50~ipad.png</string>
                 <string>AppIcon50x50@2x~ipad.png</string>
                 <string>AppIcon72x72~ipad.png</string>
                 <string>AppIcon72x72@2x~ipad.png</string>
                 <string>AppIcon76x76~ipad.png</string>
                 <string>AppIcon76x76@2x~ipad.png</string>
             </array>
         </dict>
     </dict>

This ensures that the appropriate files are copied to the right place and the correct icons are used as required by the Apple 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

### 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. 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 manually rebuild the project before trying to create an archive.