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
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'
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.
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
?
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.
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 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.
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.
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.
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!).
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.
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/**
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With