Search code examples
iosswiftxcodexcodebuildios-frameworks

Can't compile Simulator build with Universal (fat) Framework built with Xcode 10.2+


I can't compile application for Simulator (Device compiles successfully) which use Universal (fat) Framework built with Xcode 10.2+. Application use the Framework from Objective-C code. When switching from build for Device to build for Simulator Xcode stops to recognize any classes and other entities from the Framework so it doesn't compile (but in Swift files Framework classes recognized properly).

My script to create Universal Framework is similar to https://gist.github.com/sundeepgupta/3ad9c6106e2cd9f51c68cf9f475191fa (in general all such scripts use almost same logic and mostly differs by variable naming).


Solution

  • REASON

    The real reason for this issue is in Xcode compiler. Starting Xcode 10.2 Apple changed generator of Framework swift header (MyFramework.framework/Headers/MyFramework-Swift.h). Now it adds lines like

    #elif defined(__x86_64__) && __x86_64__
    #elif defined(__i386__) && __i386__
    

    to simulator header and

    #elif defined(__arm64__) && __arm64__
    #elif defined(__ARM_ARCH_7A__) && __ARM_ARCH_7A__
    

    to device header.

    So headers for simulator and device become different. Because general script for universal framework building copy header from device build directory then such framework works fine with device build but fails with Simulator builds.

    Apple discover this issue in the Xcode 10.2 release notes Known Issues chapter and suggests a solution.

    SOLUTION

    Solution to resolve the issue mentioned by Apple is to create combined header which should include both original headers from device and simulator:

    #include <TargetConditionals.h>
    #if TARGET_OS_SIMULATOR
    <contents of original iOS Simulator/Framework.framework/Framework-Swift.h>
    #else
    <contents of original iOS/Framework.framework/Framework-Swift.h>
    #endif
    

    Regarding mentioned script to make fat Framework you could modify it in next way:

    # Step 5. Convenience step to copy the framework to the project's directory
    cp -R "${UNIVERSAL_OUTPUT_DIR}/${PRODUCT_NAME}.framework" "${RELEASE_DIR}"
    
    # Step 6. Combine PRODUCT_NAME-Swift.h from device and simulator architectures (Xcode 10.2 issue: 48635615)
    UNIVERSAL_SWIFT_HEADER=${UNIVERSAL_OUTPUT_DIR}/${PRODUCT_NAME}.framework/Headers/${PRODUCT_NAME}-Swift.h
    
    > ${UNIVERSAL_SWIFT_HEADER}
    echo "#include <TargetConditionals.h>" >> ${UNIVERSAL_SWIFT_HEADER}
    echo "#if TARGET_OS_SIMULATOR" >> ${UNIVERSAL_SWIFT_HEADER}
    cat ${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PRODUCT_NAME}.framework/Headers/${PRODUCT_NAME}-Swift.h >> ${UNIVERSAL_SWIFT_HEADER}
    echo "#else" >> ${UNIVERSAL_SWIFT_HEADER}
    cat ${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PRODUCT_NAME}.framework/Headers/${PRODUCT_NAME}-Swift.h >> ${UNIVERSAL_SWIFT_HEADER}
    echo "#endif" >> ${UNIVERSAL_SWIFT_HEADER}    
    
    # Step 7. Convenience step to open the project's directory in Finder
    open "${RELEASE_DIR}"
    

    Line > ${UNIVERSAL_SWIFT_HEADER} needed to clear copied header in step 2 before combine started.