iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
📦

[iOS] How to Avoid Embedding External Frameworks in SDK Development: Configuration and Verification

に公開

This article is for Day 16 of the iOS Advent Calendar 2025.
https://qiita.com/advent-calendar/2025/ios

Introduction

While developing an in-house SDK, I needed to use an external third-party framework.

However, there was a requirement: "Do not embed the external framework within the SDK." I struggled with both the implementation and the verification method for this.

Even seasoned mobile engineers with long careers found themselves puzzled over "how the SDK side and the app side should be configured," so I decided to write this article as a reference for anyone facing the same challenge.

What This Article Covers

✅ Correct usage of "Do Not Embed" vs. "Embed & Sign" during SDK development
✅ Understanding the configuration for the SDK side and the app side
✅ Verification methods to ensure external frameworks are not embedded
✅ How to avoid CFBundleIdentifier Collision errors

Target Audience

  • iOS app developers (those using the SDK)
  • People starting SDK development
  • People struggling with framework dependencies

Conclusion (For those who want the summary)

Target Embed Setting
SDK side (referencing external framework) Do Not Embed
App side (using SDK + external framework) Embed & Sign
MySDK.framework (SDK)
└── Reference to ExternalSDK only ← Do Not Embed (Not included)

MyApp.app (App)
├── MySDK.framework       ← Embed & Sign
└── ExternalSDK.framework ← Embed & Sign

Reference: iOSDC Japan 2019 - Complete Guide to Library Import and Linking Mechanisms by Katsumi Kishikawa

https://fortee.jp/iosdc-japan-2019/proposal/28d1013f-a57b-4d42-b486-a3372c459459


Common Confusion in SDK Development

"Where should I embed what?"

Without experience in SDK development, it's easy to get confused with a structure like this:

MyProject/
├── MySDK/                        ← Framework project
│   ├── import ExternalSDK        ← import within code
│   └── ExternalSDK → ???         ← What is the setting here?

└── MyApp/                        ← Sample app
    ├── MySDK.framework           ← What is the setting here?
    └── ExternalSDK.xcframework   ← What is the setting here?

Answer: Separate the roles

MyProject/
├── MySDK/                                    ← Framework project
│   ├── import ExternalSDK                    ← import within code (to use)
│   └── ExternalSDK.xcframework → Do Not Embed ← Link only (do not include)

└── MyApp/                                    ← Sample app
    ├── MySDK.framework → Embed & Sign         ← Include
    └── ExternalSDK.xcframework → Embed & Sign ← Include

Why this configuration?

Layer Role Embed Setting
SDK Only "uses" the external framework Do Not Embed
App "Holds" what is needed at runtime Embed & Sign

Key Points:

  • The SDK only has the "right to call the APIs of the external framework."
  • The actual binary is held by the app side.
  • The framework inside the app is used at runtime.

Types of Embed Settings

When adding a framework in Xcode, there are three types of "Embed" settings.

Setting Behavior Usage
Do Not Embed Link only. Not included in the binary. When referencing an external framework on the SDK side
Embed & Sign Included in the binary and signed. When using a framework on the app side
Embed Without Signing Included in the binary but not signed. Special cases (rarely used)

Reference

Do not Embed
With the “Do Not Embed” choice you are literally saying: please don’t pack all the contents of this framework with the main application.
This implies that the final application package will not contain a folder called (by default) Frameworks with all the framework code.

Selecting "Do Not Embed" literally means instructing: "Please do not include the contents of this framework in the main application."
This implies that the final application package will not contain a folder named (by default) Frameworks, and the entire framework code will not be stored.

https://holyswift.app/frameworks-embed-or-not-embed-thats-the-question/

Why is "Do Not Embed" Necessary?

If you also set the SDK side to "Embed & Sign," a problem occurs where the framework is included twice.

❌ Incorrect Configuration (When both use Embed & Sign)

MyApp.app
├── MySDK.framework
│   └── ExternalSDK.framework  ← Included in the SDK (1st copy)
└── ExternalSDK.framework      ← Also included in the app (2nd copy)

If you upload to App Store Connect in this state, the following error will occur:

ERROR ITMS-90685: "CFBundleIdentifier Collision. 
There is more than one bundle with the CFBundleIdentifier value 
'com.example.ExternalSDK' under the application MyApp.app"

Reference

In a modularized application that uses multiple nested frameworks, when trying to publish the app to TestFlight, if we get the error stating ERROR ITMS-90685: "CFBundleIdentifier Collision.
There is more than one bundle with the CFBundleIdentifier value com.example.project under the application Project.app" means some framework has embedded another framework which the main app is also embedding.
The fix here is to go to the all other frameworks and choose not to embed any frameworks and only Embed & Sign in the main app.

In a modularized application using multiple nested frameworks, you may encounter the following error when trying to publish the app to TestFlight:
ERROR ITMS-90685: "CFBundleIdentifier Collision.

If you see the error "There is more than one bundle with the CFBundleIdentifier value com.example.project under the application Project.app," it means that a framework has embedded another framework that the main app is also embedding.

The fix in this case is to choose "Do Not Embed" for all other frameworks and only use "Embed & Sign" for the main app.

https://jsloop.net/2025/04/19/cfbundleidentifier-collision-in-ios


Correct Setup Procedures

1. SDK Side Configuration (Do Not Embed)

Steps:

  1. Open the SDK project in Xcode.
  2. Select the SDK target from TARGETS.
  3. Open the "General" tab.
  4. Check the "Frameworks, Libraries, and Embedded Content" section.
  5. Change the Embed setting of the external framework to "Do Not Embed".

Frameworks, Libraries, and Embedded Content
┌─────────────────────────────┬──────────────┐
│ Name                        │ Embed        │
├─────────────────────────────┼──────────────┤
│ ExternalSDK.xcframework     │ Do Not Embed │ ← This one
└─────────────────────────────┴──────────────┘

2. App Side Configuration (Embed & Sign)

Steps:

  1. Open the app project in Xcode.
  2. Select the app target from TARGETS.
  3. Open the "General" tab.
  4. Check the "Frameworks, Libraries, and Embedded Content" section.
  5. Set both the SDK and external framework to "Embed & Sign".

Frameworks, Libraries, and Embedded Content
┌─────────────────────────────┬──────────────┐
│ Name                        │ Embed        │
├─────────────────────────────┼──────────────┤
│ MySDK.xcframework           │ Embed & Sign │
│ ExternalSDK.xcframework     │ Embed & Sign │
└─────────────────────────────┴──────────────┘

Verification Method to Ensure It's Not Embedded

Even if you intend to set it to "Do Not Embed," you might feel anxious about whether it's truly not embedded.

You can verify this using the nm command.

What is the nm command?

nm is a command that lists the symbols (function names, class names, etc.) included in a binary.
It is a tool standardly included in macOS/Xcode, and you can use it to check what kind of symbols are contained in frameworks and libraries.

Reference:
0To view the symbols exported by your application, use the nm tool.
0This tool reads an executable’s symbol table and displays the symbol information you request.
0You can view all symbols or just the symbols from a specific segment of your executable code.
0For example, to display only the externally available global symbols, you would specify the -g option on the command line.

0To display symbols exported by an application, use the nm tool.
This tool reads the symbol table of an executable file and displays the requested symbol information.
0You can view all symbols or just symbols from a specific segment of the executable code.
0For example, to display only externally available global symbols, specify the -g option on the command line.

https://developer.apple.com/library/archive/documentation/Performance/Conceptual/CodeFootprint/Articles/ReducingExports.html

Details of nm command options
Type Meaning
T Code/Function (text section)
S Other sections
U Undefined (External reference)
D Data
  • Uppercase indicates global (external public), lowercase indicates local.

Reference:
A site describing what each symbol means.

0 Each symbol name is preceded by its value (blanks if undefined).
0 Unless the -m option is specified, this value is followed by one of the following characters, representing the symbol type:
0 U (undefined),
0 A (absolute),
0 T (text section symbol),
0 D (data section symbol),
0 B (bss section symbol),
0 C (common symbol), - (for debugger symbol table entries; see -a below),
0 S (symbol in a section other than those above), or
0 I (indirect symbol).
0 If the symbol is local (non-external), the symbol's type is instead represented by the corresponding lowercase letter.
0 A lower case u in a dynamic shared library indicates a undefined reference to a private external in another module in the same library.

https://keith.github.io/xcode-man-pages/nm.1.html

Verification Steps

1. Confirm the location of the built SDK framework

find ~/Library/Developer/Xcode/DerivedData -name "MySDK.framework" -type d 2>/dev/null

2. Check symbols with the nm command

nm -gU "/path/to/MySDK.framework/MySDK" | grep -i "external"

How to Read the Results

✅ Correct state (Not embedded)

0000000000003368 T _$s5MySDK19ExternalSDKBridgeC21onSessionTagDetect...
0000000000003b28 T _$s5MySDK19ExternalSDKBridgeC23onSessionErrorDetect...

Only the symbols for MySDK.ExternalSDKBridge (the bridge class you wrote) are displayed.

If symbols like ExternalSDK.SomeController are not displayed, it is OK.

❌ Problematic state (Embedded)

0000000000001234 T _$s11ExternalSDK14SomeControllerC...
0000000000005678 T _$s11ExternalSDK12AnotherClassC...

If symbols from the ExternalSDK itself are displayed, it has been embedded.


Points to Note

1. Xcode might change Embed settings automatically

When you add a framework, Xcode may default it to "Embed & Sign". You must always check the settings after adding one.

2. Pay attention to Build Phases as well

In addition to the "General" tab, please check the "Build Phases" → "Embed Frameworks" section. If it is listed here, it will be embedded.

Build Phases
├── Dependencies
├── Compile Sources
├── Link Binary With Libraries  ← It appears here even with "Do Not Embed"
└── Embed Frameworks            ← It will be embedded if it appears here

Key Points:

  • Appears in "Link Binary With Libraries" → It is linked (OK)
  • Appears in "Embed Frameworks" → It is embedded (NG for the SDK side)

Q&A

Q1. If I set it to "Do Not Embed" on the SDK side, won't I be unable to call functions from the external framework within the SDK?

A. You can call them.

"Do Not Embed" is strictly a setting to "not include it in the binary."
Since it is linked during compilation, it is possible to call the external framework's APIs from the SDK code.
At runtime, the external framework that was "Embed & Sign"ed on the app side will be used.

View original text

Embedding a framework that mach-o-type is a dynamic library you are assuring that all the files of that framework will be available in the final bundle of the app.
When the app try to resolve the external symbols that are not inside the main app code it will find them inside the app bundle in the framework folder.
As explained above, this could affect seriously your startup time so be careful with dynamic linking.

By embedding a framework whose mach-o-type is a dynamic library, you ensure that all files of that framework will be available in the final bundle of the app.
When the app tries to resolve external symbols that are not inside the main app code, it will find them inside the framework folder within the app bundle.
As explained above, this can seriously affect startup time, so be careful with dynamic linking.

https://holyswift.app/frameworks-embed-or-not-embed-thats-the-question/

Q2. What happens if I forget to embed the external framework on the app side?

A. It will crash at runtime.
If you see the following error, you have forgotten to "Embed & Sign" the external framework on the app side.

Configuration Errors
The following errors are caused by a project configuration that embeds a framework built for a build configuration that is different from the build configuration for the app

Dyld Error Message:
Dyld Message: Library not loaded: @rpath/<YourFrameworkName>.framework/<YourFrameworkName>
  Referenced from: <PathToYourApp>
  Reason: image not found

https://developer.apple.com/library/archive/technotes/tn2435/_index.html


Summary

Point Content
SDK side configuration Do Not Embed (Link only)
App side configuration Embed & Sign (Both SDK + external framework)
Verification method Symbol check with the nm -gU command

How to remember the configuration

The "user" side is Do Not Embed (just borrowing)
The "owner" side is Embed & Sign (holds the entity)

SDK development requires different considerations compared to regular app development.
I hope this article is helpful for those facing similar challenges.

Discussion