0

I'm splitting a largish iOS app into a few frameworks, and I'm facing the following problem:

How do I properly include a custom Cocoa Touch framework project into an iOS app project, so that everything links properly?

  1. I have created a project framework, with all the necessary code inside.
  2. To test things out, I've created a sample workspace, and added the framework project to it.
  3. Next, I've created a sample iOS app project, with a few lines of code using the framework, and added the project to the workspace as well.

Now, I get to the linking part.

First attempt:

  1. Open the app target settings in Xcode, select the Info pane, and add the framework in the Linked Frameworks and Libraries section.

  2. To make things nice, I also select the newly added framework in the app project, and change it's Location from Relative to Group to Relative to Build Products. Beneath the location shows as ../Debug-iphonesimulator/MyFramework.framework

  3. The current scheme is <Simulator, Debug>, I hit Run, and everything works as it should - the app logs that it's using the framework fine.

  4. Now, I clean the build folder, and change the scheme to <Simulator, Release>.

  5. Next, I hit Run, the projects start building, and then app linking fails:

Undefined symbols for architecture x86_64: <a function from the framework> referenced from: <a file in the app>.

Upon inspecting the build folder, I can see that the framework was built properly, resides in .../Build/Products/Release-iphonesimulator/..., and is a fat binary with both i386 and x86_64 inside.

How should I proceed to properly link the framework, so that it works in any configuration (Debug, Release), and on both the simulator and, of course, devices?

To clarify, I intend to develop both the framework(s), and the app simultaneously, so a solution like e.g. CocoaPods does it, by pre-building fat frameworks once, seems not very practical (unless I'm missing something, due to lack of sufficient experience with CocoaPods).


Below is the failing Link phase output:

Ld /Users/me/Library/Developer/Xcode/DerivedData/MyWorkspace-ahzqvfgoxbedpudjdhtqudgqzwba/Build/Intermediates/MyApp.build/Release-iphonesimulator/MyApp.build/Objects-normal/x86_64/MyApp normal x86_64
    cd /some/where/here/lives/the/workspace
    export IPHONEOS_DEPLOYMENT_TARGET=9.3
    export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++
      -arch x86_64
      -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator9.3.sdk
      -L/Users/me/Library/Developer/Xcode/DerivedData/MyWorkspace-ahzqvfgoxbedpudjdhtqudgqzwba/Build/Products/Release-iphonesimulator
      -F/Users/me/Library/Developer/Xcode/DerivedData/MyWorkspace-ahzqvfgoxbedpudjdhtqudgqzwba/Build/Products/Release-iphonesimulator
      -filelist /Users/me/Library/Developer/Xcode/DerivedData/MyWorkspace-ahzqvfgoxbedpudjdhtqudgqzwba/Build/Intermediates/MyApp.build/Release-iphonesimulator/MyApp.build/Objects-normal/x86_64/MyApp.LinkFileList
      -Xlinker
      -rpath
      -Xlinker @executable_path/Frameworks
      -mios-simulator-version-min=9.3
      -Xlinker
      -objc_abi_version
      -Xlinker 2
      -fobjc-arc
      -fobjc-link-runtime
      -stdlib=libc++
      -framework MyFramework 
      -Xlinker
      -dependency_info
      -Xlinker /Users/me/Library/Developer/Xcode/DerivedData/MyWorkspace-ahzqvfgoxbedpudjdhtqudgqzwba/Build/Intermediates/MyApp.build/Release-iphonesimulator/MyApp.build/Objects-normal/x86_64/MyApp_dependency_info.dat
      -o /Users/me/Library/Developer/Xcode/DerivedData/MyWorkspace-ahzqvfgoxbedpudjdhtqudgqzwba/Build/Intermediates/MyApp.build/Release-iphonesimulator/MyApp.build/Objects-normal/x86_64/MyApp

Undefined symbols for architecture x86_64:
  "SomeFancyFuncFromTheFramework()", referenced from:
      -[AppDelegate application:didFinishLaunchingWithOptions:] in AppDelegate.o
6
  • Have you added a build dependency in the App's target to first build the framework? Commented Jul 22, 2016 at 20:35
  • NSGod, I did try that, but it just shows a single line - the app project name; nothing to select, and nothing about the framework targets. Commented Jul 22, 2016 at 20:36
  • The MyApp_dependency_info.dat file (from the last -Xlinker arguments) contains references to both MyFramework.framework/MyFramework.tbd and MyFramework.framework/MyFramework, both inside .../Build/Products/Release-iphonesimulator/, from what I could tell (and FWIW). Commented Jul 22, 2016 at 20:48
  • 1
    Well, this is weird. I've been working all day on an iOS app where I do this very thing and it works fine, but now I just created 2 quick test projects and it doesn't seem to be working like it should (I get nothing listed like you when I try to add the framework target as a dependency)... Not sure what's going on.... Commented Jul 22, 2016 at 20:57
  • 1
    Okay, finally got it to work, though it took a bit of work. I quit out of Xcode a few times, and when I re-opened the project, things seemed to be a little better. Going to start again from scratch to see if I can do it again.... Commented Jul 22, 2016 at 21:16

1 Answer 1

1
+100

First, create the iOS framework project, being sure to set the Installation Directory (INSTALL_PATH) build setting for the framework target to @rpath. I would change the Xcode scheme to make it a shared scheme. Now close the framework project, to be sure it can be opened when we import it into the App project in the following steps.

Next, create the iOS App project. Similar to how you did, I created an App (Debug) scheme and a App (Release) scheme. I created a Frameworks group in the App project similar to the image shown below. Then I located the .xcodeproj file of my framework I just created, and dragged it into that Frameworks group. Open the disclosure triangles to reveal the framework project's Products like shown in the image below. Drag it to the Embedded Binaries section in the General section of the app target. Doing so should automatically add the other steps you would need to take to get things to work properly (I believe these include adding the framewokr as a dependency, linking against the framework, and copying the framework into the built app).

enter image description here

The last step is to make sure the framework can be found at compile time. In the app target's build settings, be sure to set Framework Search Paths (FRAMEWORK_SEARCH_PATHS) to $(BUILT_PRODUCTS_DIR). When you select the scheme for the app that builds in a Develop build style, the framework will be built in the same build style in the same directory as the app itself.

Sign up to request clarification or add additional context in comments.

4 Comments

NSGod, thanks for your answer. It's looking much better now - the project file has no references to "Debug-iphonesimulator" inside, and as a bonus no need for an explicit workspace. However, I'm still getting the same linking issues, when switching the app's scheme from Debug to Release.
Switching the framework's Run scheme to Release didn't help, strange.
Another update: it works with a dummy framework project, exactly the way you've described. Digging further to find out the reason for the original framework not working.
So the offender was a GCC_SYMBOLS_PRIVATE_EXTERN = YES in the framework's Release xcconfig. Thanks for your help, Mark.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.