When Code Signing of Frameworks Fails During macOS App Distribution, Make Sure ENABLE_BITCODE=NO

Back in December, I whipped up a test project to demonstrate why distributing a Mac app with RxSwift using Carthage failed. Building and running worked well, but I couldn’t upload the app for notarization at all. The signing step just failed.

It told me to check the distribution logs, and in IDEDistributionPipelines.log, it read:

...
2020-12-07 15:10:48 +0000  Skipping architecture thinning for item "RxTestApp" because arch "arm64e" wasn't found
2020-12-07 15:10:48 +0000  Processing step: IDEDistributionODRStep
2020-12-07 15:10:48 +0000  Processing step: IDEDistributionStripXattrsStep
2020-12-07 15:10:48 +0000  Skipping stripping extended attributes of item: <IDEDistributionItem: 0x7fd1b3698110; bundleID='io.rx.RxCocoa', path='<DVTFilePath:0x7fd24432b9e0:'/Users/ctm/Library/Developer/Xcode/Archives/2020-12-07/RxTestApp 07.12.20, 16.10.xcarchive/Products/Applications/RxTestApp.app/Contents/Frameworks/RxCocoa.framework/Versions/A'>', codeSigningInfo='<_DVTCodeSigningInformation_Path: 0x7fd2200de2b0; isSigned='1', isAdHocSigned='0', signingCertificate='<DVTSigningCertificate: 0x7fd2266baa50; name='Apple Development: Christian Tietze (933RH59P6T)', hash='893EB53E4123EE2FBF9D7C6ED10005F804F2768E', serialNumber='<DVTSigningCertificateSerialNumber: 0x7fd186ffaf50>', certificateKinds='(
    "1.2.840.113635.100.6.1.12",
    "1.2.840.113635.100.6.1.2"
), issueDate='2020-09-21 08:10:09 +0000''>', entitlements='(null)', teamID='FRMDA3XRGC', identifier='io.rx.RxCocoa', executablePath='<DVTFilePath:0x7fd2370b1710:'/Users/ctm/Library/Developer/Xcode/Archives/2020-12-07/RxTestApp 07.12.20, 16.10.xcarchive/Products/Applications/RxTestApp.app/Contents/Frameworks/RxCocoa.framework/Versions/A/RxCocoa'>', hardenedRuntime='1'>'>
2020-12-07 15:10:48 +0000  Skipping stripping extended attributes of item: <IDEDistributionItem: 0x7fd200ae2510; bundleID='io.rx.RxSwift', path='<DVTFilePath:0x7fd24433a010:'/Users/ctm/Library/Developer/Xcode/Archives/2020-12-07/RxTestApp 07.12.20, 16.10.xcarchive/Products/Applications/RxTestApp.app/Contents/Frameworks/RxSwift.framework/Versions/A'>', codeSigningInfo='<_DVTCodeSigningInformation_Path: 0x7fd203c7cd00; isSigned='1', isAdHocSigned='0', signingCertificate='<DVTSigningCertificate: 0x7fd2266baa50; name='Apple Development: Christian Tietze (933RH59P6T)', hash='893EB53E4123EE2FBF9D7C6ED10005F804F2768E', serialNumber='<DVTSigningCertificateSerialNumber: 0x7fd186f7fca0>', certificateKinds='(
    "1.2.840.113635.100.6.1.12",
    "1.2.840.113635.100.6.1.2"
), issueDate='2020-09-21 08:10:09 +0000''>', entitlements='(null)', teamID='FRMDA3XRGC', identifier='io.rx.RxSwift', executablePath='<DVTFilePath:0x7fd200cd7140:'/Users/ctm/Library/Developer/Xcode/Archives/2020-12-07/RxTestApp 07.12.20, 16.10.xcarchive/Products/Applications/RxTestApp.app/Contents/Frameworks/RxSwift.framework/Versions/A/RxSwift'>', hardenedRuntime='1'>'>
2020-12-07 15:10:48 +0000  Running /usr/bin/xattr '-crs' '/var/folders/62/8k21681d08z9lhq8h433z3rh0000gp/T/XcodeDistPipeline.~~~30iivY/Root/Applications/RxTestApp.app'
2020-12-07 15:10:49 +0000  /usr/bin/xattr exited with 0
2020-12-07 15:10:49 +0000  Processing step: IDEDistributionCodesignStep
2020-12-07 15:10:49 +0000  Entitlements for <IDEDistributionItem: 0x7fd1b3698110; bundleID='io.rx.RxCocoa', path='<DVTFilePath:0x7fd24432b9e0:'/Users/ctm/Library/Developer/Xcode/Archives/2020-12-07/RxTestApp 07.12.20, 16.10.xcarchive/Products/Applications/RxTestApp.app/Contents/Frameworks/RxCocoa.framework/Versions/A'>', codeSigningInfo='<_DVTCodeSigningInformation_Path: 0x7fd2200de2b0; isSigned='1', isAdHocSigned='0', signingCertificate='<DVTSigningCertificate: 0x7fd221196d50; name='Apple Development: Christian Tietze (933RH59P6T)', hash='893EB53E4123EE2FBF9D7C6ED10005F804F2768E', serialNumber='<DVTSigningCertificateSerialNumber: 0x7fd221456060>', certificateKinds='(
    "1.2.840.113635.100.6.1.12",
    "1.2.840.113635.100.6.1.2"
), issueDate='2020-09-21 08:10:09 +0000''>', entitlements='(null)', teamID='FRMDA3XRGC', identifier='io.rx.RxCocoa', executablePath='<DVTFilePath:0x7fd2370b1710:'/Users/ctm/Library/Developer/Xcode/Archives/2020-12-07/RxTestApp 07.12.20, 16.10.xcarchive/Products/Applications/RxTestApp.app/Contents/Frameworks/RxCocoa.framework/Versions/A/RxCocoa'>', hardenedRuntime='1'>'>: {
}
2020-12-07 15:10:49 +0000  Writing entitlements for <IDEDistributionItem: 0x7fd1b3698110; bundleID='io.rx.RxCocoa', path='<DVTFilePath:0x7fd24432b9e0:'/Users/ctm/Library/Developer/Xcode/Archives/2020-12-07/RxTestApp 07.12.20, 16.10.xcarchive/Products/Applications/RxTestApp.app/Contents/Frameworks/RxCocoa.framework/Versions/A'>', codeSigningInfo='<_DVTCodeSigningInformation_Path: 0x7fd2200de2b0; isSigned='1', isAdHocSigned='0', signingCertificate='<DVTSigningCertificate: 0x7fd2224a0da0; name='Apple Development: Christian Tietze (933RH59P6T)', hash='893EB53E4123EE2FBF9D7C6ED10005F804F2768E', serialNumber='<DVTSigningCertificateSerialNumber: 0x7fd1a668b350>', certificateKinds='(
    "1.2.840.113635.100.6.1.12",
    "1.2.840.113635.100.6.1.2"
), issueDate='2020-09-21 08:10:09 +0000''>', entitlements='(null)', teamID='FRMDA3XRGC', identifier='io.rx.RxCocoa', executablePath='<DVTFilePath:0x7fd2370b1710:'/Users/ctm/Library/Developer/Xcode/Archives/2020-12-07/RxTestApp 07.12.20, 16.10.xcarchive/Products/Applications/RxTestApp.app/Contents/Frameworks/RxCocoa.framework/Versions/A/RxCocoa'>', hardenedRuntime='1'>'> to: /var/folders/62/8k21681d08z9lhq8h433z3rh0000gp/T/XcodeDistPipeline.~~~30iivY/entitlements~~~AkwhDT
2020-12-07 15:10:49 +0000  Running /usr/bin/codesign '-vvv' '--force' '--sign' 'F1C8B9C025AADFD0F5A1C94704B713AE48E05B2D' '--entitlements' '/var/folders/62/8k21681d08z9lhq8h433z3rh0000gp/T/XcodeDistPipeline.~~~30iivY/entitlements~~~AkwhDT' '--preserve-metadata=identifier,flags,runtime' '--requirements' '=designated => anchor apple generic  and identifier "$self.identifier" and ((cert leaf[field.1.2.840.113635.100.6.1.9] exists) or ( certificate 1[field.1.2.840.113635.100.6.2.6] exists and certificate leaf[field.1.2.840.113635.100.6.1.13] exists  and certificate leaf[subject.OU] = "FRMDA3XRGC" ))' '/var/folders/62/8k21681d08z9lhq8h433z3rh0000gp/T/XcodeDistPipeline.~~~30iivY/Root/Applications/RxTestApp.app/Contents/Frameworks/RxCocoa.framework/Versions/A'
2020-12-07 15:10:49 +0000  /var/folders/62/8k21681d08z9lhq8h433z3rh0000gp/T/XcodeDistPipeline.~~~30iivY/Root/Applications/RxTestApp.app/Contents/Frameworks/RxCocoa.framework/Versions/A: replacing existing signature
2020-12-07 15:10:49 +0000  /var/folders/62/8k21681d08z9lhq8h433z3rh0000gp/T/XcodeDistPipeline.~~~30iivY/Root/Applications/RxTestApp.app/Contents/Frameworks/RxCocoa.framework/Versions/A: code object is not signed at all
2020-12-07 15:10:49 +0000  /usr/bin/codesign exited with 1

The important line:

/Frameworks/RxCocoa.framework/Versions/A: code object is not signed at all

How did that happen?

I’m on macOS Catalina and the more recent Xcode 12 toolchain includes arm64 builds for M1 Macs.

Thanks to Matteo Rattotti of https://bear.app/ for pointing out that this is a problem of the Rx project setup: ENABLE_BITCODE must be set to NO. Turns out he had to sift through their dependencies for quite a while to figure that out. Thanks for saving me hours of guess-work (on top of all the hours I was dumbfoundedly looking for a cause of this failure).

By now, most of your dependencies may already have ENABLE_BITCODE=NO set, but if that’s not the case, or if you cannot upgrade to more recent versions, you can override the setting in a fork of using your dependency manager of choice.

You can enforce this in Podfile overrides of project settings more or less like so:

post_install do |installer|
  installer.generated_projects.each do |project|
    project.targets.each do |target|
      target.build_configurations.each do |config|
		config.build_settings['ENABLE_BITCODE'] = 'NO'
      end
    end
  end
end

For Carthage, you can override settings, too, but have to use a custom shell script to make this a bit simpler, like so:

#!/usr/bin/env bash

# carthage.sh
# Usage example: ./carthage.sh build --platform iOS

set -euo pipefail

xcconfig=$(mktemp /tmp/static.xcconfig.XXXXXX)
trap 'rm -f "$xcconfig"' INT TERM HUP EXIT

# (other settings here)
echo 'ENABLE_BITCODE = NO' >> $xcconfig

export XCODE_XCCONFIG_FILE="$xcconfig"
carthage "$@"

I didn’t know you can override project settings for Carthage until building frameworks in Xcode 12 broke, but now I cannot imagine living without it – the utility of quickly trying to fix stuff by patch settings is immense.