Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 795
Martin Mlostek Avatar asked Feb 19 '21 17:02

Martin Mlostek


People also ask

How do I add files to Xcconfig?

To create an xcconfig file, choose File -> New -> File... in your project. In the new file dialog, scroll down until you see the Configuration Settings File in the Other section. You can add configurations for pretty much anything you want.

How do I distribute Swift framework?

To distribute code in binary form as a Swift package, create an XCFramework bundle, or artifact, that contains the binaries. Then, make the bundle available locally or on a server: When you host the binaries on a server, create a ZIP archive with the XCFramework in its root directory and make it available publicly.

What is XC framework?

What is XCFramework? Apple defines XCFrameworks as a distributable binary package created by Xcode that contains variants of a framework or library so that it can be used on multiple platforms (iOS, macOS, tvOS, and watchOS), including Simulator builds.

What is xcframework?

A XCFramework can wrap up a variant of a framework that would be working on iOS devices, another variant that would be working on Simulator, another one for macOS, one more for watchOS, and so on. In short, XCFramework can bundle up any framework with flavours for any platform or device that Xcode supports.

How do I create a Xc framework in Xcode?

Creating the XCFramework is just a matter of a simple command in Terminal; the xcodebuild -create-xcframework. Provided options must contain two things: The path to each framework; in this case it’s going to be the path to the framework inside each package as just said right above.

How do I link against a library in xcframework?

If your XCFramework includes a library, edit the Header Search Paths and Library Search Paths . If you link against a library that is not part of the same repo, you will need to copy over the public header files as well (in addition of the XCFramework of course!).

Why isn’t xcframeworks supported by Carthage?

Update: Boris created convinient Fastlane plugin that wraps this boilerplate up nicely. I think that this over complication can be one of the reasons why XCFrameworks are still not supported by Carthage. On a first glance usage isn’t different than how we work with standard frameworks.


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/**
like image 183
Eugene Dudnyk Avatar answered Oct 11 '22 23:10

Eugene Dudnyk