diff --git a/tests/manual/ios_assets/Assets.xcassets/AppIcon.appiconset/Contents.json b/tests/manual/ios_assets/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000000..9221b9bb1a3 --- /dev/null +++ b/tests/manual/ios_assets/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/tests/manual/ios_assets/Assets.xcassets/Contents.json b/tests/manual/ios_assets/Assets.xcassets/Contents.json new file mode 100644 index 00000000000..73c00596a7f --- /dev/null +++ b/tests/manual/ios_assets/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/tests/manual/ios_assets/Assets.xcassets/Face.imageset/Contents.json b/tests/manual/ios_assets/Assets.xcassets/Face.imageset/Contents.json new file mode 100644 index 00000000000..2108ce2651d --- /dev/null +++ b/tests/manual/ios_assets/Assets.xcassets/Face.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Face-16.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Face-32.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Face-48.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/tests/manual/ios_assets/Assets.xcassets/Face.imageset/Face-16.png b/tests/manual/ios_assets/Assets.xcassets/Face.imageset/Face-16.png new file mode 100644 index 00000000000..670beb4fbbc Binary files /dev/null and b/tests/manual/ios_assets/Assets.xcassets/Face.imageset/Face-16.png differ diff --git a/tests/manual/ios_assets/Assets.xcassets/Face.imageset/Face-32.png b/tests/manual/ios_assets/Assets.xcassets/Face.imageset/Face-32.png new file mode 100644 index 00000000000..ee159b60614 Binary files /dev/null and b/tests/manual/ios_assets/Assets.xcassets/Face.imageset/Face-32.png differ diff --git a/tests/manual/ios_assets/Assets.xcassets/Face.imageset/Face-48.png b/tests/manual/ios_assets/Assets.xcassets/Face.imageset/Face-48.png new file mode 100644 index 00000000000..f0783cd5337 Binary files /dev/null and b/tests/manual/ios_assets/Assets.xcassets/Face.imageset/Face-48.png differ diff --git a/tests/manual/ios_assets/CMakeLists.txt b/tests/manual/ios_assets/CMakeLists.txt new file mode 100644 index 00000000000..911e5a0b2c8 --- /dev/null +++ b/tests/manual/ios_assets/CMakeLists.txt @@ -0,0 +1,92 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(ios_assets LANGUAGES CXX) + +set(CMAKE_AUTOMOC ON) + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Test) + +qt_add_executable(tst_manual_ios_assets + main.cpp +) + +set_target_properties(tst_manual_ios_assets PROPERTIES + MACOSX_BUNDLE TRUE +) +target_link_libraries(tst_manual_ios_assets PRIVATE + Qt::Core + Qt::Gui + Qt::Test +) + +# Custom Info.plist +set_target_properties(tst_manual_ios_assets + PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.ios.cmake.plist") + +# Custom resources +file(GLOB_RECURSE text_files CONFIGURE_DEPENDS "*.txt") +if(text_files) + target_sources(tst_manual_ios_assets PRIVATE ${text_files}) + # On iOS the 'Resources' prefix is removed by Xcode because on iOS app bundles are shallow, + # so the final location of the text file will be + # tst_manual_ios_assets.app/textFiles/foo.txt + # On macOS the location will be + # tst_manual_ios_assets.app/Contents/Resources/textFiles/foo.txt + set_source_files_properties( + ${text_files} + PROPERTIES MACOSX_PACKAGE_LOCATION Resources/textFiles) +endif() + +# App icons +file(GLOB_RECURSE app_icons CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/appicon/AppIcon*.png") +if(IOS AND app_icons) + target_sources(tst_manual_ios_assets PRIVATE ${app_icons}) + set_source_files_properties( + ${app_icons} + PROPERTIES MACOSX_PACKAGE_LOCATION Resources) +endif() + +# Asset catalog with images +if(IOS) + enable_language(OBJCXX) + set(asset_catalog_path "${CMAKE_CURRENT_SOURCE_DIR}/Assets.xcassets") + target_sources(tst_manual_ios_assets PRIVATE "${asset_catalog_path}") + set_source_files_properties( + ${asset_catalog_path} + PROPERTIES MACOSX_PACKAGE_LOCATION Resources) + target_sources(tst_manual_ios_assets PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/utils.mm") +endif() + +# Set custom launch screen. +# iOS has evolved and provides a few ways to handle this. +# - UILaunchImageFile Info.plist key, introduced in iOS 3.2, supposedly deprecated in iOS 10. +# - UILaunchImages, Info.plist keys, introduced in iOS 7, deprecated in iOS 13 +# - UILaunchStoryboardName, Info.plist key, introduced in iOS 9, not deprecated +# - UILaunchScreen / UILaunchScreens, Info.plist dictionaries, introduced in iOS 14, not +# deprecated +# The first two expect images, the third one expects a storyboard / .xib file. +# The last ones expect a dictionary of keys to configure the launch screen. +# At the moment, UILaunchStoryboardName represents the lower bound of what Qt supports, +# so use it here. +# Reference info +# https://developer.apple.com/documentation/xcode/specifying-your-apps-launch-screen/ +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/iPhoneOSKeys.html#//apple_ref/doc/uid/TP40009252-SW24 +# https://developer.apple.com/documentation/uikit/uilocalnotification/1616660-alertlaunchimage?language=objc +# https://developer.apple.com/documentation/bundleresources/information_property_list/uilaunchimages?language=objc +# https://developer.apple.com/documentation/bundleresources/information_property_list/uilaunchstoryboardname?language=objc +# https://developer.apple.com/documentation/bundleresources/information_property_list/uilaunchscreen?language=objc +# https://forum.qt.io/topic/106251/use-launch-images-in-ios-project/4 +# https://codereview.qt-project.org/c/qt/qtdoc/+/100846 +if(IOS) + # Because we're not using the automatically generated Info.plist, it needs to be manually + # modified to have the UILaunchStoryboardName key. + set_target_properties(tst_manual_ios_assets PROPERTIES + QT_IOS_LAUNCH_SCREEN "${CMAKE_CURRENT_SOURCE_DIR}/CustomLaunchScreen.storyboard") +endif() + +# Flip to TRUE to debug +if(FALSE) + target_compile_definitions(tst_manual_ios_assets PRIVATE DEBUG_APP_DATA_LOCATION=1) +endif() diff --git a/tests/manual/ios_assets/CustomLaunchScreen.storyboard b/tests/manual/ios_assets/CustomLaunchScreen.storyboard new file mode 100644 index 00000000000..889aa39d739 --- /dev/null +++ b/tests/manual/ios_assets/CustomLaunchScreen.storyboard @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/manual/ios_assets/Info.ios.cmake.plist b/tests/manual/ios_assets/Info.ios.cmake.plist new file mode 100644 index 00000000000..ebbb6d93efa --- /dev/null +++ b/tests/manual/ios_assets/Info.ios.cmake.plist @@ -0,0 +1,93 @@ + + + + + CFBundleInfoDictionaryVersion + 6.0 + + CFBundlePackageType + APPL + + CFBundleName + $(PRODUCT_NAME) + + CFBundleDisplayName + $(PRODUCT_NAME) + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + + CFBundleExecutable + $(EXECUTABLE_NAME) + + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + CFBundleShortVersionString + $(MARKETING_VERSION) + + NSHumanReadableCopyright + + + CFBundleIconFile + + + CFBundleDevelopmentRegion + English + + LSRequiresIPhoneOS + + + UILaunchStoryboardName + CustomLaunchScreen.storyboard + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + CFBundleIcons + + CFBundlePrimaryIcon + + CFBundleIconFiles + + AppIcon29x29.png + AppIcon29x29@2x.png + AppIcon40x40@2x.png + AppIcon57x57.png + AppIcon57x57@2x.png + AppIcon60x60@2x.png + + + + CFBundleIcons~ipad + + CFBundlePrimaryIcon + + CFBundleIconFiles + + AppIcon29x29.png + AppIcon29x29@2x.png + AppIcon40x40@2x.png + AppIcon57x57.png + AppIcon57x57@2x.png + AppIcon60x60@2x.png + AppIcon29x29~ipad.png + AppIcon29x29@2x~ipad.png + AppIcon40x40~ipad.png + AppIcon40x40@2x~ipad.png + AppIcon50x50~ipad.png + AppIcon50x50@2x~ipad.png + AppIcon72x72~ipad.png + AppIcon72x72@2x~ipad.png + AppIcon76x76~ipad.png + AppIcon76x76@2x~ipad.png + + + + + diff --git a/tests/manual/ios_assets/Info.ios.qmake.plist b/tests/manual/ios_assets/Info.ios.qmake.plist new file mode 100644 index 00000000000..e97150e47b5 --- /dev/null +++ b/tests/manual/ios_assets/Info.ios.qmake.plist @@ -0,0 +1,78 @@ + + + + + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + ${ASSETCATALOG_COMPILER_APPICON_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${QMAKE_SHORT_VERSION} + CFBundleSignature + ${QMAKE_PKGINFO_TYPEINFO} + CFBundleVersion + ${QMAKE_FULL_VERSION} + LSRequiresIPhoneOS + + MinimumOSVersion + ${IPHONEOS_DEPLOYMENT_TARGET} + UILaunchStoryboardName + CustomLaunchScreen + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + CFBundleIcons + + CFBundlePrimaryIcon + + CFBundleIconFiles + + AppIcon29x29.png + AppIcon29x29@2x.png + AppIcon40x40@2x.png + AppIcon57x57.png + AppIcon57x57@2x.png + AppIcon60x60@2x.png + + + + CFBundleIcons~ipad + + CFBundlePrimaryIcon + + CFBundleIconFiles + + AppIcon29x29.png + AppIcon29x29@2x.png + AppIcon40x40@2x.png + AppIcon57x57.png + AppIcon57x57@2x.png + AppIcon60x60@2x.png + AppIcon29x29~ipad.png + AppIcon29x29@2x~ipad.png + AppIcon40x40~ipad.png + AppIcon40x40@2x~ipad.png + AppIcon50x50~ipad.png + AppIcon50x50@2x~ipad.png + AppIcon72x72~ipad.png + AppIcon72x72@2x~ipad.png + AppIcon76x76~ipad.png + AppIcon76x76@2x~ipad.png + + + + + diff --git a/tests/manual/ios_assets/appicon/AppIcon29x29.png b/tests/manual/ios_assets/appicon/AppIcon29x29.png new file mode 100644 index 00000000000..f220ad51a3d Binary files /dev/null and b/tests/manual/ios_assets/appicon/AppIcon29x29.png differ diff --git a/tests/manual/ios_assets/appicon/AppIcon29x29@2x.png b/tests/manual/ios_assets/appicon/AppIcon29x29@2x.png new file mode 100644 index 00000000000..69d8a9a3fb2 Binary files /dev/null and b/tests/manual/ios_assets/appicon/AppIcon29x29@2x.png differ diff --git a/tests/manual/ios_assets/appicon/AppIcon29x29@2x~ipad.png b/tests/manual/ios_assets/appicon/AppIcon29x29@2x~ipad.png new file mode 100644 index 00000000000..69d8a9a3fb2 Binary files /dev/null and b/tests/manual/ios_assets/appicon/AppIcon29x29@2x~ipad.png differ diff --git a/tests/manual/ios_assets/appicon/AppIcon29x29~ipad.png b/tests/manual/ios_assets/appicon/AppIcon29x29~ipad.png new file mode 100644 index 00000000000..f220ad51a3d Binary files /dev/null and b/tests/manual/ios_assets/appicon/AppIcon29x29~ipad.png differ diff --git a/tests/manual/ios_assets/appicon/AppIcon40x40@2x.png b/tests/manual/ios_assets/appicon/AppIcon40x40@2x.png new file mode 100644 index 00000000000..2966b0d8662 Binary files /dev/null and b/tests/manual/ios_assets/appicon/AppIcon40x40@2x.png differ diff --git a/tests/manual/ios_assets/appicon/AppIcon40x40@2x~ipad.png b/tests/manual/ios_assets/appicon/AppIcon40x40@2x~ipad.png new file mode 100644 index 00000000000..2966b0d8662 Binary files /dev/null and b/tests/manual/ios_assets/appicon/AppIcon40x40@2x~ipad.png differ diff --git a/tests/manual/ios_assets/appicon/AppIcon40x40~ipad.png b/tests/manual/ios_assets/appicon/AppIcon40x40~ipad.png new file mode 100644 index 00000000000..9956f67ba6d Binary files /dev/null and b/tests/manual/ios_assets/appicon/AppIcon40x40~ipad.png differ diff --git a/tests/manual/ios_assets/appicon/AppIcon50x50@2x~ipad.png b/tests/manual/ios_assets/appicon/AppIcon50x50@2x~ipad.png new file mode 100644 index 00000000000..570f8c9a508 Binary files /dev/null and b/tests/manual/ios_assets/appicon/AppIcon50x50@2x~ipad.png differ diff --git a/tests/manual/ios_assets/appicon/AppIcon50x50~ipad.png b/tests/manual/ios_assets/appicon/AppIcon50x50~ipad.png new file mode 100644 index 00000000000..9d7b8edc4fd Binary files /dev/null and b/tests/manual/ios_assets/appicon/AppIcon50x50~ipad.png differ diff --git a/tests/manual/ios_assets/appicon/AppIcon57x57.png b/tests/manual/ios_assets/appicon/AppIcon57x57.png new file mode 100644 index 00000000000..5d558bbb08a Binary files /dev/null and b/tests/manual/ios_assets/appicon/AppIcon57x57.png differ diff --git a/tests/manual/ios_assets/appicon/AppIcon57x57@2x.png b/tests/manual/ios_assets/appicon/AppIcon57x57@2x.png new file mode 100644 index 00000000000..ea14c9c0b95 Binary files /dev/null and b/tests/manual/ios_assets/appicon/AppIcon57x57@2x.png differ diff --git a/tests/manual/ios_assets/appicon/AppIcon60x60@2x.png b/tests/manual/ios_assets/appicon/AppIcon60x60@2x.png new file mode 100644 index 00000000000..14eb31e401d Binary files /dev/null and b/tests/manual/ios_assets/appicon/AppIcon60x60@2x.png differ diff --git a/tests/manual/ios_assets/appicon/AppIcon72x72@2x~ipad.png b/tests/manual/ios_assets/appicon/AppIcon72x72@2x~ipad.png new file mode 100644 index 00000000000..cb988891ffe Binary files /dev/null and b/tests/manual/ios_assets/appicon/AppIcon72x72@2x~ipad.png differ diff --git a/tests/manual/ios_assets/appicon/AppIcon72x72~ipad.png b/tests/manual/ios_assets/appicon/AppIcon72x72~ipad.png new file mode 100644 index 00000000000..a0072bcfa17 Binary files /dev/null and b/tests/manual/ios_assets/appicon/AppIcon72x72~ipad.png differ diff --git a/tests/manual/ios_assets/appicon/AppIcon76x76@2x~ipad.png b/tests/manual/ios_assets/appicon/AppIcon76x76@2x~ipad.png new file mode 100644 index 00000000000..ffa1d4729a6 Binary files /dev/null and b/tests/manual/ios_assets/appicon/AppIcon76x76@2x~ipad.png differ diff --git a/tests/manual/ios_assets/appicon/AppIcon76x76~ipad.png b/tests/manual/ios_assets/appicon/AppIcon76x76~ipad.png new file mode 100644 index 00000000000..15a13665f00 Binary files /dev/null and b/tests/manual/ios_assets/appicon/AppIcon76x76~ipad.png differ diff --git a/tests/manual/ios_assets/foo.txt b/tests/manual/ios_assets/foo.txt new file mode 100644 index 00000000000..ce013625030 --- /dev/null +++ b/tests/manual/ios_assets/foo.txt @@ -0,0 +1 @@ +hello diff --git a/tests/manual/ios_assets/ios_assets.pro b/tests/manual/ios_assets/ios_assets.pro new file mode 100644 index 00000000000..a6a610dcd8d --- /dev/null +++ b/tests/manual/ios_assets/ios_assets.pro @@ -0,0 +1,48 @@ +TEMPLATE = app +SOURCES += main.cpp +QT += core gui testlib +CONFIG += app_bundle +TARGET = tst_manual_ios_assets + +# Custom Info.plist +ios { + QMAKE_INFO_PLIST = Info.ios.qmake.plist +} + +# Custom resources +textFiles.files = $$files(*.txt) +# On iOS no 'Resources' prefix is needed because iOS app bundles are shallow, +# so the final location of the text file will be +# tst_manual_ios_assets.app/textFiles/foo.txt +# Specifying a Resources prefix actually causes code signing error for some reason. +# On macOS the location will be +# tst_manual_ios_assets.app/Contents/Resources/textFiles/foo.txt +ios { + textFiles.path = textFiles +} +macos { + textFiles.path = Contents/Resources/textFiles +} +QMAKE_BUNDLE_DATA += textFiles + +# App icons +ios { + ios_icon.files = $$files($$PWD/appicon/AppIcon*.png) + QMAKE_BUNDLE_DATA += ios_icon +} + +# Asset catalog with images +ios { + # The asset catalog needs to have an empty AppIcon.appiconset, otherwise Xcode refuses + # to compile the asset catalog. + QMAKE_ASSET_CATALOGS += Assets.xcassets + SOURCES += utils.mm + LIBS += -framework UIKit +} + +# Set custom launch screen +ios { + # Underneath, this uses QMAKE_BUNDLE_DATA, prevents the default launch screen from being set + # and bundles the custom one. + QMAKE_IOS_LAUNCH_SCREEN = $$PWD/CustomLaunchScreen.storyboard +} diff --git a/tests/manual/ios_assets/main.cpp b/tests/manual/ios_assets/main.cpp new file mode 100644 index 00000000000..020f3f940be --- /dev/null +++ b/tests/manual/ios_assets/main.cpp @@ -0,0 +1,70 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include +#include +#include + +#ifdef DEBUG_APP_DATA_LOCATION + #include +#endif + +class AssetsIos : public QObject +{ + Q_OBJECT +private slots: + void bundledTextFiles(); + void bundledAppIcons(); + void bundledImageInAssetCatalog(); +}; + +void AssetsIos::bundledTextFiles() +{ +#ifdef DEBUG_APP_DATA_LOCATION + auto paths = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); + qDebug() << paths; + + for (const QString& path: paths) { + QDir oneDir = QDir(path); + qDebug() << "path" << path << "entries" << oneDir.entryList(); + } +#endif + + /* + AppDataLocation returns the following 3 paths on iOS + /var/mobile/Containers/Data/Application/uuid/Library/Application Support/tst_manual_ios_assets + /Library/Application Support/tst_manual_ios_assets + /private/var/containers/Bundle/Application//tst_manual_ios_assets.app + + The textFiles/foo.txt file only exists in the third location at + /private/var/containers/Bundle/Application//tst_manual_ios_assets.app/textFiles/foo.txt + + AppDataLocation returns the following 3 paths on macOS + /Users//Library/Application Support/tst_manual_ios_assets + /Library/Application Support/tst_manual_ios_assets + /tst_manual_ios_assets.app/Contents/Resources + + Once again the file only exists in the third location at + /tst_manual_ios_assets.app/Contents/Resources/textFiles/foo.txt + */ + QString textFile = QStandardPaths::locate(QStandardPaths::AppDataLocation, + QStringLiteral("textFiles/foo.txt")); + QVERIFY(!textFile.isEmpty()); +} + +void AssetsIos::bundledAppIcons() { + // Confirm one of the app icons are present. + QString appIcon = QStandardPaths::locate(QStandardPaths::AppDataLocation, + QStringLiteral("AppIcon40x40@2x.png")); + QVERIFY(!appIcon.isEmpty()); +} + +bool imageExistsInAssetCatalog(QString imageName); + +void AssetsIos::bundledImageInAssetCatalog() { + // Asset catalog images can be accessed only via a name, not a path. + QVERIFY(imageExistsInAssetCatalog(QStringLiteral("Face"))); +} + +QTEST_MAIN(AssetsIos) +#include "main.moc" diff --git a/tests/manual/ios_assets/utils.mm b/tests/manual/ios_assets/utils.mm new file mode 100644 index 00000000000..c3b657084eb --- /dev/null +++ b/tests/manual/ios_assets/utils.mm @@ -0,0 +1,18 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include + +#ifdef Q_OS_IOS +#include +#endif + +bool imageExistsInAssetCatalog(QString imageName) { +#ifdef Q_OS_IOS + UIImage* image = [UIImage imageNamed:imageName.toNSString()]; + return image != nil; +#else + // Don't fail the test when building on platforms other than iOS. + return true; +#endif +}