8

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?

Martin Mlostek
  • 2,755
  • 1
  • 28
  • 57

1 Answers1

3

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/**
Eugene Dudnyk
  • 5,553
  • 1
  • 23
  • 48
  • Mh, this seems to work.. but the first time after a clean its failing.. i have extracted the script into a "sh" file and calling it in the ru-script phase like this ```../Submodules/Dependencies/provide_dependencies.sh``` any tips on how to get it running on the first build after clean? (archive is not working at all as its always clean build.. by the way the error then is "No Such module..." – Martin Mlostek Mar 01 '21 at 11:11
  • Add new Aggregate target (located in Other tab of target type picker), put the shell script build phase into Aggregate target, put the Aggregate target into the dependencies list of the iOS target – Eugene Dudnyk Mar 01 '21 at 11:16
  • well, it also happens without an extracted script.. when i paste the script directly into the phase it also fails. – Martin Mlostek Mar 01 '21 at 11:17
  • unfortunately still the same with the aggregate target – Martin Mlostek Mar 01 '21 at 11:20
  • seems like adding it as a pre-build scheme action works.. – Martin Mlostek Mar 01 '21 at 11:24
  • 1
    Cool, I'll fix this in my answer. – Eugene Dudnyk Mar 01 '21 at 11:25
  • give me 10minutes to try out all types (archive, sim, device, ...) – Martin Mlostek Mar 01 '21 at 11:26
  • will test a bit more – Martin Mlostek Mar 01 '21 at 11:30
  • is this a race condition of the build steps? – Martin Mlostek Mar 01 '21 at 11:31
  • I guess it tries to calculate compilation dependencies graph somehow, and runs actions even from dependent targets in parallel when build dependencies are not recognised. But CocoaPods create Aggregate target and it works for them. That's why it was my top-of-the-head solution to suggest. – Eugene Dudnyk Mar 01 '21 at 11:34
  • yeah.. its pretty close.. when i use the aggregate target approach, this shows "below" the real target in the build log.. i have added it as dependency in the build phases of the real target and also in the scheme.. any other place to add it to be build "before"? – Martin Mlostek Mar 01 '21 at 11:37
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/229340/discussion-between-eugene-dudnyk-and-martin-mlostek). – Eugene Dudnyk Mar 01 '21 at 11:38