How to link against the correct Framework inside a XCFramework for different platforms with xcconfig files

Viewed 973

The situation

I was just migrating to latest XCode12.4, thus I have precompiled all of my dependencies to xcframeworks. Now i am using xcconfig to link against some of these frameworks. I am having an App, that uses several precompiled xcframeworks.

Here is a shared file Dependencies/Frameworks.xcconfig that offers easy access to all the dependencies and is in the same repo as the precompiled dependencies.

/// Path into xcode specific builds
FRAMEWORKS_PATH_12_4 = Frameworks/XCode12.4
FRAMEWORKS_PATH_11_5 = Frameworks/XCode11.5 // old path, obsolete
FRAMEWORKS_PATH_10_3 = Frameworks/XCode10.3 // old path, obsolete

/// All firebase related frameworks
FIREBASE_FRAMEMWORKS = -framework FirebaseAnalytics -framework FirebaseCore -framework FirebaseCoreDiagnostics -framework FirebaseCrashlytics -framework FirebaseInstallations -framework GoogleAppMeasurement -framework GoogleDataTransport -framework GoogleUtilities -framework PromisesObjC -framework nanopb 

/// Summary of all Frameworks provided by this repo
ALL_FRAMEWORKS = -framework ZipArchive -framework Alamofire -framework RxSwift -framework RxCocoa -framework RxRelay -framework Snapkit -framework SVGKit -framework Lottie -framework AudioKit -framework NSLoggerSwift $(FIREBASE_FRAMEMWORKS)

Now my framework inside my app links against 2 frameworks, it looks like this

/// Include the dependency repo definitions
#include "../Dependencies/Frameworks.xcconfig"

/// Define this for the carthage build step
DEPENDENCIES_PATH[config=RC]         = $(SRCROOT)/../Dependencies/$(FRAMEWORKS_PATH_12_4)
DEPENDENCIES_PATH[config=Release]    = $(SRCROOT)/../Dependencies/$(FRAMEWORKS_PATH_12_4)
DEPENDENCIES_PATH[config=T1]         = $(SRCROOT)/../Dependencies/$(FRAMEWORKS_PATH_12_4)
DEPENDENCIES_PATH[config=Debug]      = $(SRCROOT)/../Dependencies/$(FRAMEWORKS_PATH_12_4)

/// Setup search paths
FRAMEWORK_SEARCH_PATHS = $(inherited) $(DEPENDENCIES_PATH)/**

/// Include required frameworks
OTHER_LDFLAGS = $(inherited) -framework Alamofire -framework ZipArchive

The problem

Now when I build or archive against generic device, all is fine. But when I build for simulator I get this error

Could not find module 'Alamofire' for target 'x86_64-apple-ios-simulator'; found: arm64, armv7-apple-ios, arm64-apple-ios, arm, armv7

I assume this is due to the recursive search path here

/// Setup search paths
FRAMEWORK_SEARCH_PATHS = $(inherited) $(DEPENDENCIES_PATH)/**

When I remove the recursive search path, I am getting this error

No such module 'Alamofire'

The workaround

What seems to work is to tell the compiler to deeply look inside the xcframework like this

/// Setup search paths
FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*] = $(inherited) $(DEPENDENCIES_PATH)/Alamofire.xcframework/ios-arm64_armv7 $(inherited) $(DEPENDENCIES_PATH)/ZipArchive.xcframework/ios-arm64_armv7
FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*] = $(inherited) $(DEPENDENCIES_PATH)/Alamofire.xcframework/ios-arm64_i386_x86_64-simulator $(DEPENDENCIES_PATH)/ZipArchive.xcframework/ios-arm64_i386_x86_64-simulator

But to be honest, I am having dozens of dependencies in the project and maintaining this is super tedious.

The question

How can I tell my frameworks xcconfig file that it should look inside the correct architectures folder inside the xcframework to find the actual framework?

1 Answers

In Xcode, add the Run Script pre-build action to your scheme.

Put this script into the Run Script pre-build action:

OLD_DIR="${PWD}"
rm -rf "${CONFIGURATION_TEMP_DIR}/XCFrameworkSlices"
mkdir -p "${CONFIGURATION_TEMP_DIR}/XCFrameworkSlices"
cd "${CONFIGURATION_TEMP_DIR}/XCFrameworkSlices"
SUFFIX="arm[v6][74]"
if [[ ! -z "$LLVM_TARGET_TRIPLE_SUFFIX" ]]; then
   SUFFIX=$LLVM_TARGET_TRIPLE_SUFFIX
fi
find $DEPENDENCIES_PATH/*.xcframework/$SWIFT_PLATFORM_TARGET_PREFIX-*$SUFFIX -type d -depth 0 -print0 | xargs -0 -I {} sh -c 'ln -s "$1" "./$(basename $(dirname \"$1\"))"' - {}
cd "${OLD_DIR}"

In xcconfig:

DEPENDENCIES_PATH_RC         = $(SRCROOT)/../Dependencies/$(FRAMEWORKS_PATH_12_4)
DEPENDENCIES_PATH_Release    = $(SRCROOT)/../Dependencies/$(FRAMEWORKS_PATH_12_4)
DEPENDENCIES_PATH_T1         = $(SRCROOT)/../Dependencies/$(FRAMEWORKS_PATH_12_4)
DEPENDENCIES_PATH_Debug      = $(SRCROOT)/../Dependencies/$(FRAMEWORKS_PATH_12_4)

DEPENDENCIES_PATH = $(DEPENDENCIES_PATH_$(CONFIGURATION))
FRAMEWORK_SEARCH_PATHS = $(inherited) $CONFIGURATION_TEMP_DIR/XCFrameworkSlices/**
Related