Incorrect imports in the generated Objective-C header when compiling for iOS simulator since Xcode 12.5 toolchain

We recently switched from Xcode 12.4 to Xcode 12.5 and encountered an interesting issue when compiling for iOS simulator. It seems that since Xcode 12.5 the swiftc compiler generates an incorrect Objective-C compatibility header under some circumstances.

Reproduction:

  1. Create the following files or clone the reproduction repository:

LibraryB.swift

import CoreGraphics
import UIKit
import Foundation

// Uncommenting the following line makes Xcode 12.5 toolchain export CoreGraphics as expected 
// public class FixMe: UIView {}

@objc public class Foo: NSObject {
	@objc public func bar() -> CGFloat { 0 }
}

LibraryA.m

@import Foundation;
@import LibraryB;

void function() {
	double bar = [[Foo new] bar];
}

build/LibraryB.framework/Modules/module.modulemap

framework module LibraryB {
	header "LibraryB-Swift.h"
}
  1. Select either Xcode 12.4 for the expected behavior or Xcode 12.5 for the unexpected behavior using DEVELOPER_DIR environment variable:
export DEVELOPER_DIR=/Applications/Xcode12.4.app/Contents/Developer
# or 
export DEVELOPER_DIR=/Applications/Xcode12.5.app/Contents/Developer
  1. Run swiftc to compile library LibraryB emitting Objective-C compatibility header into build/LibraryB.framework/Headers
swiftc LibraryB.swift -sdk ${DEVELOPER_DIR}/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk -target x86_64-apple-ios11.0-simulator -emit-library -emit-objc-header -emit-objc-header-path build/LibraryB.framework/Headers/LibraryB-Swift.h -o build/LibraryB.o
  1. Run clang to compile library LibraryA that imports LibraryB
clang LibraryA.m -fsyntax-only -x objective-c -isysroot ${DEVELOPER_DIR}/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk -target x86_64-apple-ios11.0-simulator -fmodules -F build/
  1. The expected behavior is that clang successfully compiles library LibraryA; however, since Xcode 12.5 clang fails with the following error:
While building module 'LibraryB' imported from LibraryA.m:2:
In file included from <module-includes>:1:
build/LibraryB.framework/Headers/LibraryB-Swift.h:213:4: error: expected a type
- (CGFloat)bar SWIFT_WARN_UNUSED_RESULT;
   ^
LibraryA.m:2:9: fatal error: could not build module 'LibraryB'
@import LibraryB;
 ~~~~~~~^~~~~~~~
2 errors generated.

To successfully compile with Xcode 12.5 one can create a subclass of UIView (LibraryB.swift:6) within LibraryB. When such subclass is present swiftc generates the correct Objective-C compatibility header and LibraryA compiles.

2 Likes

We've noticed this with some of our projects as well. I haven't had a chance to try to narrow it down further yet, but the common thread seems to be that it happens specifically with modules where CGFloat is the only type used from CoreGraphics, and no other frameworks that would re-export CoreGraphics are used. With Swift 5.3, the generated header contains @import CoreGraphics;, but it does not with Swift 5.4.

I've uploaded apple/swift#37416 as a possible fix for this.

1 Like

Is there any semi-good workarounds for this?

You can try creating a UIView subclass somewhere within LibraryB and it should compile.

It has to be visible from objc, doesn't it? So it has to have unique @objc name, and you can't just have one file and compile it to every module?