0

I have a iOS app that has "Framework A" as a subproject and includes it as a target dependency. "Framework A" has "Framework B" has a subproject and includes it as a target dependency. Finally "Framework B" has "Framework C" as a subproject and includes it as a target dependency.

This project builds fine and works perfectly in the Simulator but blows up immediately with the error dyld: Library not loaded: @rpath/Framework A.framework/Framework A.

The problem is that the "Framework A.framework" file is not automatically copied into the app's executable. You can add a "Copy File" build phase to your app target and specify that "Framework A.framework" should be embedded into the executable but this solution doesn't scale. You are still missing "Framework B.framework" and "Framework C.framework".

For example, imagine you have a complex dependency structure like described in the question What is the correct process for linking static libraries that have common static libraries?. You would have to manually embed six frameworks into your executable and if the root framework added any more dependencies the project wouldn't build anymore. You'd have to remember to manually embed any new dependencies.

So does anyone know if there is a way for Xcode to automatically embed your target dependency frameworks (like it does with static libraries) or why Apple doesn't do this? Especially with iOS it seems absolutely pointless to have Xcode build frameworks to the build directory and then do absolutely nothing with them.

Community
  • 1
  • 1
Reid Main
  • 3,394
  • 3
  • 25
  • 42

2 Answers2

1

I couldn't find any way to get Xcode to do this automatically. In the end I updated up writing a script that I added as a post build phase

frameworksDirectory=${CONFIGURATION_BUILD_DIR}/${CONTENTS_FOLDER_PATH}/Frameworks
mkdir -p "${frameworksDirectory}"

for framework in "${CONFIGURATION_BUILD_DIR}"/*.framework
do
    if [ -d "${framework}" ]; then
        cp -r "${framework}" "${frameworksDirectory}"
    fi
done

for framework in "${CONFIGURATION_BUILD_DIR}/../Release${EFFECTIVE_PLATFORM_NAME}"/*.framework
do
    if [ -d "${framework}" ]; then
        cp -r "${framework}" "${frameworksDirectory}"
    fi
done
Reid Main
  • 3,394
  • 3
  • 25
  • 42
-1

The short answer to how to automatically copy frameworks is to use a dependency manager. A dependency manager is required in order to do it correctly. The only other answer is to avoid multi-level dependencies (which is generally preferred, but not always possible).

Your script only works in cases that only one version of each framework is built. It doesn't detect the case of multiple versions of the same framework being built, and can lead to undefined behavior without warnings if that happens. You may have special knowledge of your build system to know that there are no conflicting sub-frameworks, but Xcode doesn't have that. Proving there are no conflicts would mean building a dependency manager.

In my opinion the need for a dependency manager is a sign of an overcomplicated use of frameworks. But there are cases where it is useful.

You shouldn't just copy everything and hope it works (which is one reason Xcode doesn't). In my opinion, the best dependency manager right now is Carthage. The most popular dependency manager right now is CocoaPods.

For some more discussion on a more specialized version of this question, see Why are umbrella frameworks discouraged?.

Community
  • 1
  • 1
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • It does handle multiple versions because there is no way multiple versions of the framework can be in the build directory since they will have identical names. I actually do have multiple versions of the same framework being built in the project that is using this script. Also I do know about dependency managers like CocoaPods and Carthage I am choosing not to use them in this situation. – Reid Main May 23 '15 at 19:57
  • When you build the identically named frameworks, don't they overwrite each other? How do you reliably know which one will be included? That's what I mean by undefined behavior. – Rob Napier May 23 '15 at 20:16
  • Gotcha I understand now. You are correct that if you don't validate that all the same frameworks are being built then you will have that undefined behaviour. Everything is pulling the frameworks from common submodule pointers so in this case it is impossible. But the core of the question remains even if no frameworks were duplicated I still needed a way for all the dependent frameworks to get copied into the binary. – Reid Main May 23 '15 at 21:52
  • Right. And the only general way to do that is with a dependency manager. If you have a special case where you know "it'll all be fine" then your script is fine, but I'd argue you're as better off to just add copy steps at the top so that you have a single point to check that assumption. (Or use a dependency manager.) The lack of scalability is really in the "it'll all be fine" assumption, not in the "I have to add a copy step" work. – Rob Napier May 23 '15 at 21:56
  • I'm having trouble understanding this as an *answer* and not a lengthy comment in reply to the self-answer. Can you clarify which parts are intended to stand alone? – Nathan Tuggy May 24 '15 at 01:35
  • @NathanTuggy you make a reasonable point. Edited to move the core of the answer to the top. The commentary is to explain why it is the answer. – Rob Napier May 24 '15 at 13:06