Issue Description

Whenever I run upload_to_testflight with fastlane in a capacitor-powered app environment, I get the errors mentioned in the title of this issue and listed below:

    ----------------------------------
    --- Step: upload_to_testflight ---
    ----------------------------------
    Creating authorization token for App Store Connect API
    Ready to upload new build to TestFlight (App: xxxxxx)...
    Going to upload updated app to App Store Connect
    This might take a few minutes. Please don't interrupt the script.
    Transporter transfer failed.
    
    ERROR ITMS-90713: Missing Info.plist value. A value for the Info.plist key 'CFBundleIconName' is missing in the bundle 'xxxx'. Apps built with iOS 11 or later SDK must supply app icons in an asset catalog and must also provide a value for this Info.plist key. For more information see http://help.apple.com/xcode/mac/current/#/dev10510b1f7.
    ERROR ITMS-90022: Missing required icon file. The bundle does not contain an app icon for iPhone / iPod Touch of exactly '120x120' pixels, in .png format for iOS versions >= 10.0. To support older versions of iOS, the icon may be required in the bundle outside of an asset catalog. Make sure the Info.plist file includes appropriate entries referencing the file. See https://developer.apple.com/documentation/bundleresources/information_property_list/user_interface
    ERROR ITMS-90023: Missing required icon file. The bundle does not contain an app icon for iPad of exactly '167x167' pixels, in .png format for iOS versions supporting iPad Pro. To support older operating systems, the icon may be required in the bundle outside of an asset catalog. Make sure the Info.plist file includes appropriate entries referencing the file. See https://developer.apple.com/documentation/bundleresources/information_property_list/user_interface
    ERROR ITMS-90023: Missing required icon file. The bundle does not contain an app icon for iPad of exactly '152x152' pixels, in .png format for iOS versions >= 10.0. To support older operating systems, the icon may be required in the bundle outside of an asset catalog. Make sure the Info.plist file includes appropriate entries referencing the file. See https://developer.apple.com/documentation/bundleresources/information_property_list/user_interface
    ERROR ITMS-90704: Missing App Icon. An app icon measuring 1024 by 1024 pixels in PNG format must be included in the Asset Catalog of apps built for iOS, iPadOS, or watchOS. Without this icon, apps cannot be submitted for review. For details, see https://developer.apple.com/ios/human-interface-guidelines/icons-and-images/app-icon/.

Tried and Failed attempts

Xcode files

I double-checked the icons, and to me, they seem to be correct:

image

The files are also visible in XCode without any warning sign in the asset catalog section:

image

Target Membership is also set correctly to App.

The settings visible below seem to be valid as well:

image

Contents.json

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

I've created them using a third-party tool, mentioned here: https://stackoverflow.com/a/45122603/1238150, called "Icon Set Creator."

Info.plist

My Info.plist looks like this:

image

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
      "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>CFBundleDevelopmentRegion</key>
        <string>de</string>
        <key>CFBundleDisplayName</key>
        <string>$(PRODUCT_BUNDLE_NAME)</string>
        <key>CFBundleExecutable</key>
        <string>$(EXECUTABLE_NAME)</string>
        <key>CFBundleIdentifier</key>
        <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
        <key>CFBundleInfoDictionaryVersion</key>
        <string>6.0</string>
        <key>CFBundleName</key>
        <string>$(PRODUCT_NAME)</string>
        <key>CFBundlePackageType</key>
        <string>APPL</string>
        <key>CFBundleShortVersionString</key>
        <string>$(MARKETING_VERSION)</string>
        <key>CFBundleIconName</key>
        <string>AppIcon</string>
        <key>CFBundleURLTypes</key>
        <array>
            <dict>
                <key>CFBundleTypeRole</key>
                <string>Editor</string>
                <key>CFBundleURLName</key>
                <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
                <key>CFBundleURLSchemes</key>
                <array>
                    <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
                </array>
            </dict>
        </array>
        <key>CFBundleVersion</key>
        <string>$(CAPACITOR_PROJECT_VERSION)</string>
        <key>ITSAppUsesNonExemptEncryption</key>
        <false />
        <key>UILaunchStoryboardName</key>
        <string>LaunchScreen</string>
        <key>UIMainStoryboardFile</key>
        <string>Main</string>
        <key>UIRequiredDeviceCapabilities</key>
        <array>
            <string>armv7</string>
        </array>
        <key>UIStatusBarStyle</key>
        <string>UIStatusBarStyleDarkContent</string>
        <key>UISupportedInterfaceOrientations</key>
        <array>
            <string>UIInterfaceOrientationPortrait</string>
        </array>
        <key>UISupportedInterfaceOrientations~ipad</key>
        <array>
            <string>UIInterfaceOrientationPortrait</string>
            <string>UIInterfaceOrientationPortraitUpsideDown</string>
            <string>UIInterfaceOrientationLandscapeLeft</string>
            <string>UIInterfaceOrientationLandscapeRight</string>
        </array>
        <key>UIUserInterfaceStyle</key>
        <string>Light</string>
        <key>UIViewControllerBasedStatusBarAppearance</key>
        <true />
    
        <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
        <string>Add your description for requiring permission here</string>
    
        <key>NSLocationAlwaysUsageDescription</key>
        <string>Add your description for requiring permission here</string>
    
        <key>NSLocationWhenInUseUsageDescription</key>
        <string>Add your description for requiring permission here</string>
    </dict>
    </plist>

project.pbxproj

This file contains references to the four schemes I've created development, staging, beta and production. Each of them has at least the following build setting applied:

    buildSettings = {
        ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
        ...
        INFOPLIST_FILE = App/Info.plist;
        ...
    }

Here's the full config of the staging environment used in the case I describe:

    F8AD9F41291D56E00xxxxxx /* Staging */ = {
                isa = XCBuildConfiguration;
                buildSettings = {
                    ALWAYS_SEARCH_USER_PATHS = NO;
                    ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
                    CLANG_ANALYZER_NONNULL = YES;
                    CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
                    CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
                    CLANG_CXX_LIBRARY = "libc++";
                    CLANG_ENABLE_MODULES = YES;
                    CLANG_ENABLE_OBJC_ARC = YES;
                    CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
                    CLANG_WARN_BOOL_CONVERSION = YES;
                    CLANG_WARN_COMMA = YES;
                    CLANG_WARN_CONSTANT_CONVERSION = YES;
                    CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                    CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                    CLANG_WARN_EMPTY_BODY = YES;
                    CLANG_WARN_ENUM_CONVERSION = YES;
                    CLANG_WARN_INFINITE_RECURSION = YES;
                    CLANG_WARN_INT_CONVERSION = YES;
                    CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
                    CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
                    CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                    CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
                    CLANG_WARN_STRICT_PROTOTYPES = YES;
                    CLANG_WARN_SUSPICIOUS_MOVE = YES;
                    CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                    CLANG_WARN_UNREACHABLE_CODE = YES;
                    CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
                    CODE_SIGN_IDENTITY = "iPhone Developer";
                    COPY_PHASE_STRIP = NO;
                    DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
                    ENABLE_NS_ASSERTIONS = NO;
                    ENABLE_STRICT_OBJC_MSGSEND = YES;
                    GCC_C_LANGUAGE_STANDARD = gnu11;
                    GCC_NO_COMMON_BLOCKS = YES;
                    GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
                    GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
                    GCC_WARN_UNDECLARED_SELECTOR = YES;
                    GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
                    GCC_WARN_UNUSED_FUNCTION = YES;
                    GCC_WARN_UNUSED_VARIABLE = YES;
                    IPHONEOS_DEPLOYMENT_TARGET = 13.0;
                    MTL_ENABLE_DEBUG_INFO = NO;
                    SDKROOT = iphoneos;
                    SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
                    VALIDATE_PRODUCT = YES;
                };
                name = Staging;
            };
            F8AD9F42291D56E000xxxxxx /* Staging */ = {
                isa = XCBuildConfiguration;
                baseConfigurationReference = 088FDA9807EE2E8C50xxxxxx /* Pods-App.staging.xcconfig */;
                buildSettings = {
                    ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
                    CLANG_ENABLE_MODULES = YES;
                    CODE_SIGN_IDENTITY = "iPhone Distribution";
                    CODE_SIGN_STYLE = Manual;
                    CURRENT_PROJECT_VERSION = "$(CAPACITOR_PROJECT_VERSION)";
                    DEVELOPMENT_TEAM = xxxxx;
                    INFOPLIST_FILE = App/Info.plist;
                    IPHONEOS_DEPLOYMENT_TARGET = 13.0;
                    "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.2;
                    LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
                    MARKETING_VERSION = "$(CAPACITOR_VERSION_NAME)";
                    PRODUCT_BUNDLE_IDENTIFIER = "${CAPACITOR_APP_ID}";
                    PRODUCT_BUNDLE_NAME = "${CAPACITOR_APP_NAME}";
                    PRODUCT_NAME = "$(TARGET_NAME)";
                    PROVISIONING_PROFILE_SPECIFIER = "match AppStore xx.xx.xx";
                    "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "";
                    SUPPORTS_MACCATALYST = YES;
                    SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
                    SWIFT_VERSION = 5.0;
                    TARGETED_DEVICE_FAMILY = "1,2";
                };
                name = Staging;
            };

App.ipa

I even investigated the generated app file and its package content, and the Info.plist contained CFBundleIconName when I added it but it made no difference.

The App.ipa I am referring to was created with fastlane's build_ios_app (aka gym) action, not with XCodes GUI.

Environment

fastlane environment

### Stack

| Key                         | Value                                       |
| --------------------------- | ------------------------------------------- |
| OS                          | 12.4                                        |
| Ruby                        | 3.1.2                                       |
| Bundler?                    | false                                       |
| Git                         | git version 2.38.0                          |
| Installation Source         | ~/.asdf/installs/ruby/3.1.2/bin/fastlane    |
| Host                        | macOS 12.4 (21F79)                          |
| Ruby Lib Dir                | ~/.asdf/installs/ruby/3.1.2/lib             |
| OpenSSL Version             | OpenSSL 3.0.5 5 Jul 2022                    |
| Is contained                | false                                       |
| Is homebrew                 | false                                       |
| Is installed via Fabric.app | false                                       |
| Xcode Path                  | /Applications/Xcode.app/Contents/Developer/ |
| Xcode Version               | 13.4.1 
| Swift Version               | 5.6.1                                       |

This is the content of my Fastfile

    # Inspired by
    # - https://capgo.app/blog/automatic-capacitor-ios-build-github-action/
    
    default_platform(:ios)
    
    config = JSON.parse(File.read('../App/capacitor.config.json'))
    
    keychain_name = 'fastlane-certificates'
    keychain_password = 'xxx'
    
    def delete_temp_keychain(name)
      # https://docs.fastlane.tools/actions/delete_keychain
      delete_keychain(name:) if File.exist? File.expand_path("~/Library/Keychains/#{name}-db")
    end
    
    def create_temp_keychain(name, password)
      # https://docs.fastlane.tools/actions/create_keychain/
      create_keychain(
        name:,
        password:,
        unlock: false,
        timeout: 0
      )
    end
    
    def ensure_temp_keychain(name, password)
      delete_temp_keychain(name)
      create_temp_keychain(name, password)
    end
    
    platform :ios do
      before_all do |_lane, _options|
        setup_ci if ENV['CI']
      end
    
      after_all do |_lane, _options|
        delete_temp_keychain(keychain_name)
      end
    
      error do |_lane, _exception, _options|
        delete_temp_keychain(keychain_name)
      end
    
      private_lane :buildNextApp do |options|
        UI.message('Build App...')
        yarn(
          command: "build:web:#{options[:target]}",
          package_path: "#{Dir.pwd}/../../../package.json"
        )
        UI.message('Built App')
      end
    
      private_lane :getApiKey do
        app_store_connect_api_key(
          key_id: 'xx',
          issuer_id: 'xx',
          key_filepath: "#{Dir.pwd}/xx.p8",
          duration: 1200, # optional (maximum 1200)
          in_house: false # optional but may be required if using match/sigh
        )
      end
    
      lane :stagingBuild do
        buildNextApp(target: 'staging')
      end
    
      desc 'Push a new staging build to TestFlight'
      lane :staging do
        stagingBuild
    
        ensure_temp_keychain(keychain_name, keychain_password)
    
        match(
          type: 'appstore',
          app_identifier: [config['appId']],
          api_key: getApiKey,
          readonly: false,
          fail_on_name_taken: true,
          keychain_name:,
          keychain_password:
        )
    
        # (https://docs.fastlane.tools/actions/build_ios_app/)
        build_ios_app(
          clean: true,
          configuration: 'Staging',
          export_method: 'app-store',
          scheme: 'staging',
          silent: true,
          workspace: 'App.xcworkspace'
        )
    
        upload_to_testflight(
          app_identifier: config['appId'],
          ipa: "#{Dir.pwd}/../App.ipa",
          skip_waiting_for_build_processing: true,
          expire_previous_builds: true,
          skip_submission: true,
          submit_beta_review: false,
          distribute_external: false,
          notify_external_testers: false
        )
    
      end
    end

And the one from my AppFile

    require 'json'
    
    config = JSON.parse(File.read('../App/capacitor.config.json'))
    
    app_identifier(config['appId'])
    # apple_id("[[APPLE_ID]]") # Your Apple Developer Portal username
    itc_team_id('xxx') # App Store Connect Team ID (https://appstoreconnect.apple.com/WebObjects/iTunesConnect.woa/ra/user/detail)
    team_id('xxx') # Developer Portal Team ID (https://developer.apple.com/account/#MembershipDetailsCard)

I spent hours now and have no clue what's wrong...

After adding the setting below the upload works, but this seems to to obfuscate the error instead of resolving it...

    ENV['ITMSTRANSPORTER_FORCE_ITMS_PACKAGE_UPLOAD'] = 'true'

This workaround is mentioned in several other not-related issues reported by others in the fastlane GitHub repository:

To me applying this workaround seems risky and flaky over time... That is why I am still looking for a valid and reliable solution to my issue.


If you read this far - thank you! I hope you read this far because you either have a solution or you are in the same boat as I am and hope we can find a solution together! :D

0

There are 0 answers