.swiftinterface files: When are they generated and where are they stored?

Hey folks! Following up on a great WWDC session yesterday that ran out of time:

My problem:
I am building a tool that:

  1. takes any module/library that can be imported & used in Swift code.
    It should support everything from the Swift stdlib to Apple's Objective-C libraries e.g. CoreLocation

  2. generates a module with a 100% equivalent API. Not trying to replicate the functionality/behavior of the module at all, just the compile-time interface. In other words, somebody should be able to swap out the original library for the generated one without having to change their code at all, and everything should still compile.

My questions:

  1. Assuming I have a .swiftinterface file for the library, is there any information lost/not present there that would prevent me from being able to generate an equivalent module (as discussed above)?

  2. For what kinds of dependencies is a .swiftinterface file not going to be available? What about Objective-C/C++/C libraries?

  3. In those scenarios, are there internal/private ways I could theoretically take advantage of to generate a .swiftinterface file (or equivalent)?

  4. For pre-built scenarios where a .swiftinterface file should exist, how do I find it? To start with, for the Swift stdlib and iOS libraries e.g. CoreLocation

generates a module with a 100% equivalent API. Not trying to replicate the functionality/behavior of the module at all, just the compile-time interface. In other words, somebody should be able to swap out the original library for the generated one without having to change their code at all, and everything should still compile.

The code wouldn't need to change but the configuration would need to change so that the old code links against the mocked library instead of the original library. This also applies to ObjC. If some code is linking against CakeKit, and you're creating a separate MockCakeKit (would need to be named CakeKit for zero code changes, but disambiguating here for clarity), then you need to make sure that Clang also links against MockCakeKit and not CakeKit.

Information from headers is not going to be available. E.g. NSView is defined in NSView.h, not in AppKit's swiftinterface.

I think it might be possible to do this using swift-ide-test, e.g. you may want to look at the tests under test/IDE marked print_*. (cc @blangmuir)

cd "$(xcrun -sdk iphoneos --show-sdk-path)/System/Library/Frameworks/CoreLocation.framework"
find . -name "CoreLocation.swiftmodule" -type d

For the stdlib, the swiftmodule is in a different location. Generally, you can run fd or find in the Xcode.app directory for ModuleName.swiftmodule. The standard library is called Swift.swiftmodule.

Do you need to link to this code? Generated Swift interfaces for ObjC code do not account for differences in symbol names between ObjC and Swift, so you will not get a compatible link-time interface between code built using the .h files and code built using the generated interface.

Also, while you may in some cases be able to get code that compiles using the Swift generated interface for ObjC code, that's not what the generated interface is designed for, and there may be problems we haven't even attempted to solve.

Thanks for the response! No, I don't need to link a pre-compiled binary to the mocked dependency, I'm intending to recompile the target source against the mocked dependencies.

So, the requirement is just that the .swiftinterface would have enough information to allow me to generate a source-compatible mock (a.k.a. without requiring changes to the target source to use the mock).

Would you expect the swift-ide-test tooling to satisfy this?

Appreciate the response. Taking each part at a time:

The code wouldn't need to change but the configuration would need to change so that the old code links against the mocked library instead of the original library. This also applies to ObjC. If some code is linking against CakeKit, and you're creating a separate MockCakeKit (would need to be named CakeKit for zero code changes, but disambiguating here for clarity), then you need to make sure that Clang also links against MockCakeKit and not CakeKit.

Definitely. It seems that for the stdlib (since as I mentioned in my other response, I'm re-compiling the target source), if there is e.g. a class named String in another library Foo that is also imported in the file, it automatically disambiguates to Foo.String not Swift.String. And for other library dependencies, I'm rewriting the Package.swift/Podfile/etc.

cd "$(xcrun -sdk iphoneos --show-sdk-path)/System/Library/Frameworks/CoreLocation.framework" find . -name "CoreLocation.swiftmodule" -type d

For the stdlib, the swiftmodule is in a different location. Generally, you can run fd or find in the Xcode.app directory for ModuleName.swiftmodule . The standard library is called Swift.swiftmodule .

Thanks, that worked for the stdlib one. However, /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.5.sdk/System/Library/Frameworks/CoreLocation.framework doesn't contain any .swiftinterface files for me.

I'm on 11.3.1, am I doing something wrong there?

My bad, I should've double-checked before writing it. It looks like CoreLocation.swiftmodule is also under $SDKDIR/usr/lib/swift for some reason.