187

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?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
spassas
  • 4,778
  • 2
  • 31
  • 39
  • 8
    A temporary workaround (that is working for me) is to use the legacy build system https://stackoverflow.com/a/54058682 – Tom Knapen Dec 15 '20 at 13:23
  • 132
    Another workaround is to set BuildSettings ->"Validate Workspace" to Yes. It will still show a warning, but will build the project. – o15a3d4l11s2 Dec 15 '20 at 16:22
  • 6
    "Validate Workspace" to YES works for me! But when I change to NO again it still works. Any idea? – Hen Shabat Dec 16 '20 at 14:55
  • 4
    @o15a3d4l11s2 What are the implications of using this option? – Nic Hubbard Dec 16 '20 at 16:41
  • 5
    @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. – o15a3d4l11s2 Dec 17 '20 at 08:26
  • 1
    My solution for now is to revert back to Xcode 12.2. – Nic Hubbard Dec 17 '20 at 22:09
  • @HenShabat Change it to Yes (Error), and you will get the error again, it seems that the default setting is Yes (Error). – user8127814 Dec 18 '20 at 16:34
  • 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: https://download.developer.apple.com/Developer_Tools/Xcode_12.2/Xcode_12.2.xip Oh and make sure you choose "Xcode 12.2" in the Xcode Preferences -> Locations -> Command Line Tools dropdown after starting xcode 12.2. – SudoPlz Dec 22 '20 at 00:10
  • Be sure to select 'All' on the ' BuildSettings' tab to find 'Validate Workspace' if it's not showing up. – j2abro May 04 '22 at 18:09

4 Answers4

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Alex Shubin
  • 3,549
  • 1
  • 27
  • 32
  • 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 :) – spassas Dec 16 '20 at 06:51
  • Please see my answer https://stackoverflow.com/a/65613941/1431182 for a build script that I now use instead of the old `lipo` based approach – mistahenry Jan 07 '21 at 14:13
  • xcrun xcodebuild -create-xcframework -framework /path/to/ios.framework -framework /path/to/sim.framework -output combined.xcframework – Cameron Lowell Palmer Jan 28 '21 at 08:38
  • @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. – Ravi Kumar Feb 10 '21 at 08:38
  • getting this error: binaries with multiple platforms are not supported – Muhammad Shauket Jul 19 '21 at 08:41
  • very much helpful, short 3 mins quick video is here : https://youtu.be/TCnhvHUcjrY – Mushrankhan Aug 30 '21 at 06:51
  • Regarding Rome, we're very close to adding support for XCFrameworks! – Mark Oct 14 '21 at 19:58
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

Srikanth
  • 1,861
  • 17
  • 29
  • 21
    Looks promising and makes sense but I still get the same error when implementing this solution :-( – Arik Segal Dec 15 '20 at 15:34
  • 2
    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. – Chris Dec 15 '20 at 17:31
  • Same. I have verified that we're excluding the proper frameworks as reported by `lipo` and no dice. – SlashDevSlashGnoll Dec 15 '20 at 18:57
  • 1
    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. – lifjoy Dec 15 '20 at 21:29
  • 4
    Did the trick for me by just selecting `Any iOS Simulator SDK` without inserting any values on the TARGET (not PROJECT) section. – CularBytes Dec 23 '20 at 13:34
  • 10
    ...and under PROJECT under Build Settings search for Validate Workspace, put Debug on Yes. – CularBytes Dec 23 '20 at 15:24
  • Not working for ZendeskSDK on ObjC project. – Sabrina Dec 27 '20 at 17:39
  • 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://stackoverflow.com/a/65308739/4577504 – Diego Jiménez Jun 10 '22 at 09:30
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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
mistahenry
  • 8,554
  • 3
  • 27
  • 38
  • Thanks for the answer, that's basically what I do at the moment. However, this is a workaround with many limitations... – spassas Dec 15 '20 at 13:38
  • @spassas 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"? – mistahenry Dec 15 '20 at 13:46
  • 1
    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 pipelines – spassas Dec 15 '20 at 13:54
  • 1
    fair enough, those would certainly be poor limitations if I had them as well. Let's hope for a quick resolution by Apple :) – mistahenry Dec 15 '20 at 13:56
  • 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 @mistahenry – Declan Land Dec 15 '20 at 15:22
  • this is the answer that worked for me. Thanks @mistahenry – xyzlast Jan 11 '21 at 10:23
  • I'm assuming this is still an issue, because here I am. Thanks for the workaround, it's something at least. – swiftyboi Jan 20 '22 at 22:43
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
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Paul Bancarel
  • 259
  • 1
  • 3
  • Cool! is there a way to do step 1 automatically as well ? – Rubycon Dec 19 '20 at 09:41
  • 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? – jxd Dec 23 '20 at 14:00
  • 11
    I now use this solution, which is much simpler and more convenient: https://stackoverflow.com/a/65306886/2003634 – jxd Dec 23 '20 at 14:07
  • Why not just bind framework name via `input files list`? – user28434'mstep Jul 23 '21 at 17:07