How to consume a dynamic Xcode framework?

I have an Xcode project setup as follows:

  • 3 static libraries
  • 1 framework target, whose Mach-O type is set to Dynamic Library
  • main app target (iOS app)

The target dependencies are as follows:

  • In framework's build phase [Link with libraries], I have the 3 libs statically linked.
  • In the main app's build phase [Link with libraries], I have only the framework, which is dynamically linked.

As per my understanding:

  • The libs are statically linked to the framework. So, the framework binary would contain code from all the libs.
  • The framework is dynamically linked to the main app (iOS app in this case). So, the main app's binary only has a reference to the framework's binary, which would be loaded in the memory at runtime.

Assuming my understanding is correct, I'm stuck with the following problem:

  • All 3 libs build successfully
  • The framework builds successfully
  • The main app target doesn't build. The compilation is successful, but the build fails with linker errors.

Please let me know if I am doing something incorrectly, or if a configuration is missing. Below are more details:

The linker gives the following error:

Undefined symbols for architecture arm64:
  "StringUtils.GetStr() -> Swift.String", referenced from:
      dynamic_fw.AppDelegate.application(_: __C.UIApplication, didFinishLaunchingWithOptions: [__C.UIApplicationLaunchOptionsKey : Any]?) -> Swift.Bool in AppDelegate.o
  "TWUtils.GetNum() -> Swift.Int", referenced from:
      dynamic_fw.AppDelegate.application(_: __C.UIApplication, didFinishLaunchingWithOptions: [__C.UIApplicationLaunchOptionsKey : Any]?) -> Swift.Bool in AppDelegate.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

And the command shown in the logs for linking phase is:

Ld /Users/raunit.shrivastava/Library/Developer/Xcode/DerivedData/dynamic-fw-foqtqhpopkmoapfufzxbfloamnpr/Build/Products/Debug-iphonesimulator/dynamic-fw.app/dynamic-fw normal (in target 'dynamic-fw' from project 'dynamic-fw')
    cd /Users/raunit.shrivastava/Desktop/dynamic-fw
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -Xlinker -reproducible -target arm64-apple-ios17.5-simulator -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator17.5.sdk -O0 -L/Users/raunit.shrivastava/Library/Developer/Xcode/DerivedData/dynamic-fw-foqtqhpopkmoapfufzxbfloamnpr/Build/Intermediates.noindex/EagerLinkingTBDs/Debug-iphonesimulator -L/Users/raunit.shrivastava/Library/Developer/Xcode/DerivedData/dynamic-fw-foqtqhpopkmoapfufzxbfloamnpr/Build/Products/Debug-iphonesimulator -L. -L./StringUtils -L./TWFramework -L./TWUtils -L./dynamic-fw -F/Users/raunit.shrivastava/Library/Developer/Xcode/DerivedData/dynamic-fw-foqtqhpopkmoapfufzxbfloamnpr/Build/Intermediates.noindex/EagerLinkingTBDs/Debug-iphonesimulator -F/Users/raunit.shrivastava/Library/Developer/Xcode/DerivedData/dynamic-fw-foqtqhpopkmoapfufzxbfloamnpr/Build/Products/Debug-iphonesimulator -F. -F./StringUtils -F./TWFramework -F./TWUtils -F./dynamic-fw -filelist /Users/raunit.shrivastava/Library/Developer/Xcode/DerivedData/dynamic-fw-foqtqhpopkmoapfufzxbfloamnpr/Build/Intermediates.noindex/dynamic-fw.build/Debug-iphonesimulator/dynamic-fw.build/Objects-normal/arm64/dynamic-fw.LinkFileList -Xlinker -rpath -Xlinker @executable_path/Frameworks -Xlinker -rpath -Xlinker ./\*\* -dead_strip -Xlinker -object_path_lto -Xlinker /Users/raunit.shrivastava/Library/Developer/Xcode/DerivedData/dynamic-fw-foqtqhpopkmoapfufzxbfloamnpr/Build/Intermediates.noindex/dynamic-fw.build/Debug-iphonesimulator/dynamic-fw.build/Objects-normal/arm64/dynamic-fw_lto.o -Xlinker -export_dynamic -Xlinker -no_deduplicate -Xlinker -objc_abi_version -Xlinker 2 -fobjc-link-runtime -L/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphonesimulator -L/usr/lib/swift -Xlinker -add_ast_path -Xlinker /Users/raunit.shrivastava/Library/Developer/Xcode/DerivedData/dynamic-fw-foqtqhpopkmoapfufzxbfloamnpr/Build/Intermediates.noindex/dynamic-fw.build/Debug-iphonesimulator/dynamic-fw.build/Objects-normal/arm64/dynamic_fw.swiftmodule -Xlinker -sectcreate -Xlinker __TEXT -Xlinker __entitlements -Xlinker /Users/raunit.shrivastava/Library/Developer/Xcode/DerivedData/dynamic-fw-foqtqhpopkmoapfufzxbfloamnpr/Build/Intermediates.noindex/dynamic-fw.build/Debug-iphonesimulator/dynamic-fw.build/dynamic-fw.app-Simulated.xcent -Xlinker -sectcreate -Xlinker __TEXT -Xlinker __ents_der -Xlinker /Users/raunit.shrivastava/Library/Developer/Xcode/DerivedData/dynamic-fw-foqtqhpopkmoapfufzxbfloamnpr/Build/Intermediates.noindex/dynamic-fw.build/Debug-iphonesimulator/dynamic-fw.build/dynamic-fw.app-Simulated.xcent.der -framework TWFramework -Xlinker -no_adhoc_codesign -Xlinker -dependency_info -Xlinker /Users/raunit.shrivastava/Library/Developer/Xcode/DerivedData/dynamic-fw-foqtqhpopkmoapfufzxbfloamnpr/Build/Intermediates.noindex/dynamic-fw.build/Debug-iphonesimulator/dynamic-fw.build/Objects-normal/arm64/dynamic-fw_dependency_info.dat -o /Users/raunit.shrivastava/Library/Developer/Xcode/DerivedData/dynamic-fw-foqtqhpopkmoapfufzxbfloamnpr/Build/Products/Debug-iphonesimulator/dynamic-fw.app/dynamic-fw
Answered by DTS Engineer in 857732022

When you link the framework, you need to tell the linker what symbols to export. Without that:

  • The symbols don’t get exported.
  • And if no one inside the framework uses the symbols, the code might be dead stripped.

There are various ways that you can control how the linker decides on what symbols to export. My preferred option is -exported_symbols_list, which gives you exact control over what symbols get exported. However, there are numerous other ways to do this. Search the ld man page for export to see them all.

Finally, for more background to all of this, see An Apple Library Primer and the documentation it references.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

When you link the framework, you need to tell the linker what symbols to export. Without that:

  • The symbols don’t get exported.
  • And if no one inside the framework uses the symbols, the code might be dead stripped.

There are various ways that you can control how the linker decides on what symbols to export. My preferred option is -exported_symbols_list, which gives you exact control over what symbols get exported. However, there are numerous other ways to do this. Search the ld man page for export to see them all.

Finally, for more background to all of this, see An Apple Library Primer and the documentation it references.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thanks for the flag. I used the nm -m framework_binary command to check what symbols are currently being exposed by the framework. Below is my framework's header file:

#import <Foundation/Foundation.h>

//! Project version number for TWFramework.
FOUNDATION_EXPORT double TWFrameworkVersionNumber;

//! Project version string for TWFramework.
FOUNDATION_EXPORT const unsigned char TWFrameworkVersionString[];

// In this header, you should import all the public headers of your framework using statements like #import <TWFramework/PublicHeader.h>
// how to include a generated swift header here?

the output of nm -m framework_binary is

0000000000003ff8 (__TEXT,__const) external _TWFrameworkVersionNumber
0000000000003fc8 (__TEXT,__const) external _TWFrameworkVersionString

My static libraries consist of swift files, I am not sure how to include them to the main header file. As per my understanding, the swift methods are automatically exposed as long as they are marked as public. So, how do I tell the framework's header file to export the publicly defined swift methods from the static libraries present in the framework's dependencies?

PS: I have a single Xcode project, and each library and framework exists as a separate build target in the same project.

How to consume a dynamic Xcode framework?
 
 
Q