3
votes

I am attempting to convert an old statically linked library to a framework. With mixed swift and objective c in the static library, all headers are generated correctly. However, switching to a framework target and adding swift files, marked with the @objc header, the class is not added to the -Swift.h header. I can import the header, but swift classes are not found. This is in Xcode 10.2 and attempted using both Swift 4.2 and 5.

Are there any particular settings in XCode that will affect the generation of the *-Swift.h header in a mixed Objective C/Swift framework target?

2
You do something wrong.Amin Negm-Awad
@AminNegm-Awad, that is a duh comment, the question is "what might I be doing wrong"JDL
Oh, my comment is not precise enough? What about your Q? My answer is the best you can expect from your Q.Amin Negm-Awad
@AminNegm-Awad, If there are additional details that I could provide that would make it a better question, please elucidate. I have provided the details that I thought pertinent after searching all available questions on the subject. If you do not know enough about XCode to understand the problem or possible solutions, feel free to let the question pass you by. Actually the question at hand is, "Are there any particular settings in XCode that will affect the generation of the *-Swift.h header in a mixed Objective C/Swift framework target?"JDL
@AminNegm-Awad, There are only about 4-6 settings in Xcode that appear to affect the module generation, that does not seem "broad" to me. Anyways, I have posted a possible solution after much tinkering with and reading about the build system.JDL

2 Answers

2
votes

I had a similar issue. In my case it was a known issue in Xcode 10.2:

https://developer.apple.com/documentation/xcode_release_notes/xcode_10_2_release_notes

If you’re building a framework containing Swift code and using lipo to create a binary that supports both device and simulator platforms, you must also combine the generated Framework-Swift.h headers for each platform to create a header that supports both device and simulator platforms. (48635615) ...

In my case all I had to do was to update the Carthage to the newest version 0.33.0

2
votes

The problem appears to be a combination of Apple's new build system, the expectations they set when compiling and the number of inter-dependencies in the project setup.

The new build system runs the Swift compilations in parallel. When having multiple library/framework dependencies that are mixed Objective C and Swift, the compiler appears to not generate the -Swift.h files on time. In static libraries, the -Swift.h files appear to be generated at the end of the Swift Compilation process, meaning they are not generated quickly enough to be used by the Objective C files when the Objective C compilation occurs. When generating a framework, it appears that the Compiler generates the header at the beginning of the compilation process and the Swift files are not fully compiled and the -Swift.h file does not generate appropriately with the Objective C class interfaces and protocols.

What this means ends up meaning is that we can not rely on the "target dependencies" to build the dependent projects correctly.

So how can we build our .framework of mixed Objective C and -Swift.h without a ton of manual scripting.

Here are the tricks I discovered that will work.

  1. Use the old build system. When using the new build system there is an error when it attempts to merge the module map for the static library file saying that it can not find the *-Swift.h file whether or not it exists.
  2. Create your framework by making it a wrapper around the static library by:
    • Giving them both the same product name and module name.
    • Add a single .Swift file to the framework build so that it has something to compile and will link the swift libraries.
    • link to the static library in the framework.
    • Add all necessary headers to the public headers of the framework.
    • Add all public headers to the umbrella header.
    • Use a Run script phase to copy the *-Swift.h file from the static library build to the framework product post compile.
    • For any public headers that include the *-Swift.h, you may need to post process the header and replace the *-Swift.h import with the appropriate framework import ie . This would not be recommended due to possible cyclical imports in the umbrella header.
  3. When running a clean, build the framework target first manually, before building the application target.

Below is an example script for copying the *-Swift.h file post build.

header_file="${TARGET_TEMP_DIR}/../${PRODUCT_MODULE_NAME}.build/DerivedSources/${PRODUCT_MODULE_NAME}-Swift.h"
header_dir="${BUILT_PRODUCTS_DIR}/${PUBLIC_HEADERS_FOLDER_PATH}"
mkdir -p "$DIR"
echo "copying $header_file $header_dir"
cp -f "$FILE" "$DIR"

UPDATED

Marking all modules Swift compilation mode to "Whole Module" appears to have a positive affect on this issue, but I have not fully tested it.