Xcode 12.3: Building for iOS Simulator, but the linked and embedded framework was built for iOS + iOS Simulator [duplicate]
Asked Answered
P

4

188

I have an app using a linked and embedded custom framework. The app built properly for iOS devices and simulators until Xcode 12.2. Starting from Xcode 12.3 however, I'm getting the following error:

Building for iOS Simulator, but the linked and embedded framework 'My.framework' was built for iOS + iOS Simulator.

The framework is built for both devices and simulators (as the error actually says) and merged using lipo, so it should be able to run everywhere without issues.

Am I missing something here? Is there a relevant change in Xcode 12.3?

Parameter answered 15/12, 2020 at 9:39 Comment(9)
A temporary workaround (that is working for me) is to use the legacy build system stackoverflow.com/a/54058682Hamburg
Another workaround is to set BuildSettings ->"Validate Workspace" to Yes. It will still show a warning, but will build the project.Fiorin
"Validate Workspace" to YES works for me! But when I change to NO again it still works. Any idea?Fraase
@Fiorin What are the implications of using this option?Lamoreaux
@NicHubbard, it is a rather tricky case. Switching it to "Yes" causes a pre-build validation process. In this particular case it finds that the framework is "fat" (containing both iOS and simulator architecture inside) and produces a validation warning. Why this helps - because now this problem is a warning and it does not appear again as error. Only errors will stop the build process. Overall I suggest this as a temporary workaround and not a permanent solution.Fiorin
My solution for now is to revert back to Xcode 12.2.Lamoreaux
@HenShabat Change it to Yes (Error), and you will get the error again, it seems that the default setting is Yes (Error).Emptyhanded
Don't even bother with workarounds. Until the erroneous framework is built as an XCFramework, just revert to Xcode 12.2: Here's the link to download it: download.developer.apple.com/Developer_Tools/Xcode_12.2/… Oh and make sure you choose "Xcode 12.2" in the Xcode Preferences -> Locations -> Command Line Tools dropdown after starting xcode 12.2.Tessin
Be sure to select 'All' on the ' BuildSettings' tab to find 'Validate Workspace' if it's not showing up.Soldo
A
74

I'm afraid that this is actually the correct error and the framework shouldn't contain iOS and iOS Simulator code at the same time. Apple tries to force us to use XCFrameworks for this purpose. They started it in Xcode 11 and just tightened up the restrictions.

The only correct way to resolve this is to rebuild the framework as an XCFramework. Which is easy to do:

xcrun xcodebuild -create-xcframework \
    -framework /path/to/ios.framework \
    -framework /path/to/sim.framework \
    -output combined.xcframework

You can start with a combined .framework. Make two copies of the framework, and use lipo to remove the slices from the binary that are associated with a different SDK.

It is based on the original answer from Apple here.

My particular case is that I'm getting this error using Rome, which produces these frameworks (a possible solution is here). Also, a lot of struggling is going on on the Carthage side.

Atreus answered 15/12, 2020 at 23:11 Comment(7)
Great, thanks a lot. I'm using a workaround at the moment but I will rebuild my framework as XCFramework at the first opportunity. If everything works, I will also accept the answer :)Parameter
Please see my answer https://mcmap.net/q/16495/-building-for-ios-simulator-but-the-linked-framework-39-framework-39-was-built-for-ios for a build script that I now use instead of the old lipo based approachRefute
xcrun xcodebuild -create-xcframework -framework /path/to/ios.framework -framework /path/to/sim.framework -output combined.xcframeworkAlialia
@alex-shubin I'm facing the below mentioned problem, I'm using both architecture arm64 and armv7. ravihmalviya@Mac-mini linphone-sdk % xcrun xcodebuild -create-xcframework \ -framework x86_64-apple-darwin.ios/Frameworks/bctoolbox-ios.framework \ -framework arm64-apple-darwin.ios/Frameworks/bctoolbox-ios.framework \ -framework armv7-apple-darwin.ios/Frameworks/bctoolbox-ios.framework \ -output bctoolbox-ios.xcframework Error -> Both ios-arm64 and ios-armv7 represent two equivalent library definitions.Craftsman
getting this error: binaries with multiple platforms are not supportedIves
very much helpful, short 3 mins quick video is here : youtu.be/TCnhvHUcjrYNutty
Regarding Rome, we're very close to adding support for XCFrameworks!Airliner
P
37

You have to exclude device architectures while building for simulator and while building for the device you have to exclude simulator's architectures.

To do that, navigate to Build Settings of your project -> Excluded Architectures -> Select the configuration(Debug/Release/Etc...) -> Tap + -> Any iOS Simulator SDK -> Add arm64, arm64e, armv7

Similarly, add x86_64, i386 to Any iOS SDK.

enter image description here

PS: You can check all the architectures which are present in your framework by running file <path_to_framework_binary> or lipo -info <path_to_framework_binary>.

Ex. file /Users/srikanth.kv/MyProject/MyLibrary.framework/MyLibrary

Primo answered 15/12, 2020 at 14:11 Comment(8)
Looks promising and makes sense but I still get the same error when implementing this solution :-(Daytoday
Does not work for me. I'm guessing there's something else going on as I'm running both xcode 12.3 and 12.2. I can build for device and simulator on 12.2 but I get the error building for device or simulator on code 12.3.Mcclenon
Same. I have verified that we're excluding the proper frameworks as reported by lipo and no dice.Peculiarize
Didn't work for me on M1 Mac building my iOS app as target "My Mac Designed for iPad", but the Validate Workspace trick was a good workaround. This Excluded Architectures trick did work for the same project on my Intel-based iMac Pro.Felisafelise
Did the trick for me by just selecting Any iOS Simulator SDK without inserting any values on the TARGET (not PROJECT) section.Cacka
...and under PROJECT under Build Settings search for Validate Workspace, put Debug on Yes.Cacka
Not working for ZendeskSDK on ObjC project.Jenaejenda
One question, is it possible to add a script to Xcode that could do this check and set the correct project architectures? Answer my own question, here: https://mcmap.net/q/16316/-xcode-12-3-building-for-ios-simulator-but-the-linked-and-embedded-framework-was-built-for-ios-ios-simulator-duplicateChantal
R
28

I have a framework with a universal binary that contains x86_64 and arm64 which I merge with lipo with a custom script at framework build time. I encountered this same issue for Xcode 12.3 and have created a work around for now. Hopefully this will get fixed in Xcode quickly, but until then, one quick fix would be to thin the architectures and use the framework that you need.

Please see my answer here on how to start producing .xcframeworks which is the long term solution for framework authors

For instance, let's assume I'm in a terminal in the working directory where my universal framework some_framework.framework is. If I want to run on an actual physical device, I execute the following command:

lipo -thin arm64 some_framework.framework/some_framework -output some_framework

With the above command, you extract the arm64 binary. Afterwards, replace the current some_framework.framework/some_framework with the newly generated arm64 only binary

mv some_framework some_framework.framework

If you have a universal framework built only from Objective-C sources, your job is done. But if you've got Swift code too, then you would need to update some_framework.framework/Modules/some_framework.swiftmodule so that it does not contain any references to architectures that are not arm64.

You would follow a similar process for running on the simulator except that you need x86_64. I'm currently now maintaining two versions of my framework until this is fixed. Whenever I switch between the simulator and the device, I simply switch out which framework is in my project.

Refute answered 15/12, 2020 at 13:30 Comment(7)
Thanks for the answer, that's basically what I do at the moment. However, this is a workaround with many limitations...Parameter
@Parameter well until lead to believe otherwise, I'm going to assume this is a bug in the latest Xcode that will be resolved in relatively short order. Besides inconvenience, what do you view as the other "many limitations"?Refute
yes, I also assume it's an Xcode bug. A first important limitation is that when different teams work on the framework and the main app in parallel and have isolated access on the code, the coordination becomes very cumbersome. Another one is that it breaks CI testing and distribution pipelinesParameter
fair enough, those would certainly be poor limitations if I had them as well. Let's hope for a quick resolution by Apple :)Refute
This is the answer that worked for me. In my project i'm importing a framework called Intercom that was supported for both iOS+iOS Simulator. It's disappointing that this is the answer.. the new set of Xcode updates are absolutely terrible. Thanks anyway @RefuteMeritocracy
this is the answer that worked for me. Thanks @RefuteArnold
I'm assuming this is still an issue, because here I am. Thanks for the workaround, it's something at least.Langdon
S
22

In addition to mistahenry's answer, you can handle this automatically in your project with this workaround.

  1. Set your Universal framework that does not work in Xcode 12.3 to Do not embed (in GeneralFrameworks, Libraries and Embedded Content)

  2. Add this "new run script phase" in "Build Phases":

    FRAMEWORK_APP_PATH="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
    
    # 1. Copying FRAMEWORK to FRAMEWORK_APP_PATH
    find "$SRCROOT" -name '*.framework' -type d | while read -r FRAMEWORK
    do
    if [[ $FRAMEWORK == *"MY_WONDERFUL_UNIVERSAL_FRAMEWORK.framework" ]]
    then
        echo "Copying $FRAMEWORK into $FRAMEWORK_APP_PATH"
        cp -r $FRAMEWORK "$FRAMEWORK_APP_PATH"
    fi
    done
    # 2. Loops through the frameworks embedded in the application and removes unused architectures.
    find "$FRAMEWORK_APP_PATH" -name '*.framework' -type d | while read -r FRAMEWORK
    do
    if [[ $FRAMEWORK == *"MY_WONDERFUL_UNIVERSAL_FRAMEWORK.framework" ]]
    then
    
        echo "Strip invalid archs on: $FRAMEWORK"
        FRAMEWORK_EXECUTABLE_NAME=$(/usr/libexec/PlistBuddy -c "Print CFBundleExecutable" "$FRAMEWORK/Info.plist")
        FRAMEWORK_EXECUTABLE_PATH="$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME"
        echo "Executable is $FRAMEWORK_EXECUTABLE_PATH"
        EXTRACTED_ARCHS=()
        for ARCH in $ARCHS
        do
        echo "Extracting $ARCH from $FRAMEWORK_EXECUTABLE_NAME"
        lipo -extract "$ARCH" "$FRAMEWORK_EXECUTABLE_PATH" -o "$FRAMEWORK_EXECUTABLE_PATH-$ARCH"
        EXTRACTED_ARCHS+=("$FRAMEWORK_EXECUTABLE_PATH-$ARCH")
        done
        echo "Merging extracted architectures: ${ARCHS}"
        lipo -o "$FRAMEWORK_EXECUTABLE_PATH-merged" -create "${EXTRACTED_ARCHS[@]}"
        rm "${EXTRACTED_ARCHS[@]}"
        echo "Replacing original executable with thinned version"
        rm "$FRAMEWORK_EXECUTABLE_PATH"
        mv "$FRAMEWORK_EXECUTABLE_PATH-merged" "$FRAMEWORK_EXECUTABLE_PATH"
        codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements $FRAMEWORK_EXECUTABLE_PATH
    else
        echo "Ignored strip on: $FRAMEWORK"
    fi
    done
    
  • Replace MY_WONDERFUL_UNIVERSAL_FRAMEWORK by the name of your framework and be sure that it is located at SRCROOT
Skylight answered 15/12, 2020 at 15:22 Comment(4)
Cool! is there a way to do step 1 automatically as well ?Casto
Is there a way to do this for ALL (previously) embedded frameworks in Xcode 12.3, instead of having separate of these workaround-run scripts for each of the frameworks?Fulk
I now use this solution, which is much simpler and more convenient: https://mcmap.net/q/16495/-building-for-ios-simulator-but-the-linked-framework-39-framework-39-was-built-for-iosFulk
Why not just bind framework name via input files list?Queasy

© 2022 - 2024 — McMap. All rights reserved.