.xcframework
swift .xcframework script Estimated reading time: 13 minutes- Common Approaches to Code Sharing
- What is an XCFramework?
- Possible Solutions
- Script
- Pitfalls
- Strip unused arch
- Conclusion:
- Resources
As developers increasingly target multiple Apple platforms—iOS, macOS, iPadOS, watchOS, and tvOS—there’s a growing need to share code across these environments. Sharing code not only improves development efficiency but also ensures consistency across apps, reduces maintenance efforts, and speeds up updates.
To meet this need, developers often create reusable frameworks. However, the challenge lies in building a single framework that works seamlessly across different devices and architectures, such as physical iOS devices, iOS simulators, and macOS apps (via Mac Catalyst). Achieving this involves complex processes, including architecture-specific builds and platform compatibility handling.
Common Approaches to Code Sharing
There are several methods for building reusable frameworks to support multiple platforms:
Separate Frameworks for Each Platform:
- Developers create individual frameworks for each platform (e.g., one for iOS, another for Mac Catalyst).
- Challenges: Increased manual effort, redundancy in maintenance, and potential mismatches between versions for different platforms.
Universal Frameworks:
- These frameworks combine all supported architectures into a single binary.
- Challenges: Universal frameworks are no longer supported in the latest Apple toolchains for iOS 15 and above due to new restrictions.
XCFrameworks:
- Apple introduced XCFrameworks in Xcode 11 to provide a robust solution for multi-platform development.
- Benefits: Fully supported by Apple, easy to distribute, and capable of packaging slices for multiple architectures and platforms into a single distributable framework.
From these options, XCFrameworks stand out as the best approach for modern Apple development due to their flexibility and official support.
What is an XCFramework?
An XCFramework is a new framework packaging format introduced by Apple to address the limitations of Universal Frameworks. It allows developers to bundle binary slices for multiple platforms and architectures into a single distributable package. This format makes it easy to create a single framework that works seamlessly across:
- iOS Devices: Built for ARM architectures.
- iOS Simulators: Built for x86_64 or ARM64 architectures.
- Mac Catalyst: Built for Mac applications using Catalyst technology.
Key Features of XCFrameworks:
- Multi-Platform Support: Combine binaries for iOS, macOS, watchOS, and tvOS into a single framework.
- Multi-Architecture Compatibility: Package slices for ARM, x86_64, and ARM64 architectures.
- Simplified Distribution: Developers can distribute a single XCFramework file that contains all required binaries for various platforms.
- Built-In Toolchain Support: Apple provides official support for creating and using XCFrameworks via xcodebuild.
How XCFrameworks Work
An XCFramework bundles multiple framework slices, each tailored for specific platforms and architectures, into a single distributable package. This ensures compatibility across various Apple ecosystems, including iOS, macOS, watchOS, and tvOS. Here’s a conceptual breakdown:
XCFramework
├── iOS (Device)
│ └── Architecture: arm64
│ └── Framework Binary
│ └── Resources (e.g., images, storyboards)
├── iOS (Simulator)
│ └── Architecture: x86_64, arm64
│ └── Framework Binary
│ └── Resources
├── macOS (Catalyst)
│ └── Architecture: x86_64, arm64
│ └── Framework Binary
│ └── Resources
├── watchOS
│ └── Device and Simulator (arm64, x86_64)
│ └── Framework Binary
│ └── Resources
├── tvOS
│ └── Device and Simulator (arm64, x86_64)
│ └── Framework Binary
│ └── Resources
├── Metadata
│ └── Info.plist (Defines the structure and included slices)
Possible Solutions
An XCFramework is a packaging format introduced by Apple to address the need for a multi-platform framework that works on different devices and architectures. However, building an XCFramework is not as simple as running a single command. It involves building separate archives for each platform, which are then combined into the final XCFramework. There are a few ways to accomplish this:
Manual Approach:
- The manual method involves using Xcode’s command-line tool (xcodebuild) to archive the framework for each platform separately. Afterward, you manually combine the generated archives into an XCFramework.
- While this approach gives developers complete control over the process, it is tedious, error-prone, and requires significant time and effort, especially when you need to support multiple platforms like iOS devices, iOS simulators, and Mac Catalyst.
Xcode Project Setup with Multiple Targets:
- You can configure your Xcode project to have multiple targets for each platform. This setup allows you to use Xcode’s build system to create platform-specific archives. However, managing these targets and ensuring all configurations are correct can be complex and requires a good understanding of Xcode.
- Although this method can be streamlined using a custom build script, it still requires some manual intervention and does not provide a fully automated solution.
Automated Build Scripts:
- Automating the process using a build script is the most efficient and reliable method for creating an XCFramework. This method reduces human error, speeds up the process, and allows you to integrate the build process into your CI/CD pipeline.
- Using a script, you can automate the entire process: cleaning the build environment, building archives for different platforms, and creating the final XCFramework. This approach ensures that the process is repeatable and scalable.
Script
Inspiration
This script was inspired by the work of Phillip Jacobs
’ Create-XCFramework, which provides a foundational approach to automating the creation of XCFrameworks. The original idea and structure laid the groundwork for a streamlined and efficient build process, and this script builds upon that inspiration by adding additional features such as enhanced error handling, time tracking, and user-friendly visual feedback.
I’ve also incorporated platform-specific archives and automated cleanup to further optimize the workflow for iOS and Mac Catalyst development.
The output may be as below:
khb@MacBook-Pro-kyryl MySDK % sh create-xcframework.sh
• Resetting build environment...
• [Reset] Build environment cleaned in 0s. •
• Building archive for destination: generic/platform=iOS...
• [Archived] generic/platform=iOS slice created in 35s. •
• Building archive for destination: generic/platform=iOS Simulator...
• [Archived] generic/platform=iOS Simulator slice created in 38s. •
• Creating XCFramework...
• [XCFramework] Created in 2s at: ./archives/xcframework/MySDK.xcframework •
• [Build Complete] XCFramework is ready at: ./archives/xcframework/MySDK.xcframework •
* .--.
/ / `
+ | |
' \ \__,
* + '--' *
+ /\
+ .' '. *
* /======\ +
;:. _ ;
|:. (_) |
|:. _ |
+ |:. (_) | *
;:. ;
.' \:. / `.
/ .-'':._.'`-. \
|/ /||\ \|
jgs _..--"""````"""--.._
_.-'`` ``'-._
-'
FRAMEWORK BUILD COMPLETED SUCCESSFULLY
Total time spent: 75s
Pitfalls
-
Incompatible Architectures
Problem: The XCFramework may fail to work if incompatible architectures (e.g., ARM64, x86_64) are included for certain platforms.
Solution: Ensure you explicitly build slices for each platform and architecture using the correct destination in xcodebuild (e.g., generic/platform=iOS for devices, generic/platform=iOS Simulator for simulators). Exclude unsupported architectures using the
EXCLUDED_ARCHS
build setting where necessary. -
Framework Not Found Error
Problem: Consumers of the XCFramework encounter “Framework not found” errors during integration.
Solution: Ensure that the framework is properly embedded in the app target using the Embed Frameworks build phase in Xcode. Verify that the
DYLD_LIBRARY_PATH
andLD_RUNPATH_SEARCH_PATHS
settings include the path to the embedded framework. -
Duplicate Symbol Errors
Problem: Duplicate symbol errors may arise when combining different framework binaries into an XCFramework.
Solution: Use the
SKIP_INSTALL
=NO andBUILD_LIBRARY_FOR_DISTRIBUTION
=YES settings when archiving. This ensures that the framework binaries are built for distribution without symbol conflicts. -
Framework is Not ABI-Compatible
Problem: The XCFramework may fail on certain platforms or Xcode versions due to ABI (Application Binary Interface) incompatibilities.
Solution: Always set
BUILD_LIBRARY_FOR_DISTRIBUTION
=YES to enable module stability and ensure ABI compatibility for Swift frameworks. -
Large XCFramework Size
Problem: The XCFramework may become very large due to the inclusion of multiple architecture slices.
Solution: Optimize the size by removing debug symbols using the strip command or by setting
DEBUG_INFORMATION_FORMAT
to dwarf-with-dsym for release builds. -
Mac Catalyst Integration Issues
Problem: The Mac Catalyst slice may cause build or runtime errors due to missing settings or unsupported APIs.
Solution: Explicitly enable Mac Catalyst support in your Xcode project by selecting Mac in the Deployment Info section. Test the framework on a Mac Catalyst app to ensure compatibility.
-
Lack of Swift Compatibility
Problem: If the XCFramework includes Swift code, it may break when used with a different Swift compiler version.
Solution: Always build the XCFramework with
BUILD_LIBRARY_FOR_DISTRIBUTION
=YES to make it module-stable across different Swift versions. -
Failure to Distribute Resources
Problem: Resources (e.g., images, storyboards) included in the framework are not accessible after integration.
Solution: Use a resource bundle and include it in the XCFramework. Ensure consumers include the resource bundle in their app target.
-
Code Signing Issues
Problem: Code signing errors when using the XCFramework in a signed app.
Solution: Build the framework without signing (
CODE_SIGN_IDENTITY
=””CODE_SIGNING_REQUIRED
=NO) to avoid conflicts during XCFramework creation. Let the consuming app handle code signing during the final build. -
Debugging Challenges
Problem: Difficulties debugging issues in an XCFramework due to stripped symbols or lack of debugging tools.
Solution: Ensure
DEBUG_INFORMATION_FORMAT
is set to dwarf-with-dsym for debug builds. Distribute the dSYM files along with the XCFramework for debugging purposes. -
Integration Issues in CI/CD
Problem: CI/CD pipelines may encounter errors when using XCFrameworks due to dependency resolution issues.
Solution: Use tools like CocoaPods, Carthage, or Swift Package Manager to automate XCFramework integration. Ensure the build script for CI includes steps to resolve and embed dependencies.
-
Limited Support for Older Xcode Versions
Problem: XCFrameworks may not be supported in older Xcode versions (pre-Xcode 11).
Solution: Clearly document the minimum required Xcode version for consuming the XCFramework. For older projects, consider providing fallback universal frameworks where feasible.
-
Misconfigured Build Settings
Problem: Incorrect build settings (e.g., deployment targets, library types) can cause runtime errors or missing symbols.
Solution: Set the Minimum Deployment Target to the lowest version you wish to support. Double-check that the framework type is set to Dynamic Framework if dynamic linking is needed.
Strip unused arch
To reduce the size of a framework, we could remove unused architectures from it.
How to Use This Script in Xcode
-
Add a New Run Script Phase:
- Open your Xcode project.
- Select your target in the Project Navigator.
- Go to the Build Phases tab.
- Click the “+” button and select New Run Script Phase.
-
Paste the Script:
- Copy the script above and paste it into the Run Script text field.
-
Configure Options:
- Ensure Input Files and Output Files are left empty unless needed for specific workflows.
- Enable Show environment variables in build log if you want detailed output during the build.
-
Build Your Project:
- Run your project as usual.
- The script will automatically strip unused architectures from embedded frameworks during the build process.
Script:
Conclusion:
Building an XCFramework can be a complex and error-prone task, especially when targeting multiple platforms. This automated build script provides the best solution by simplifying and streamlining the process. By automating the entire workflow, it saves time, ensures consistency, and improves the overall development experience.
For iOS and macOS developers who need to create XCFrameworks, this script is the optimal solution for handling cross-platform compatibility, automating the build process, and improving workflow efficiency.
Resources
- Apple Developer: Distributing Binary Frameworks as XCFrameworks
- Apple Developer: Creating an XCFramework
- Raywenderlich: How to Create an XCFramework
- Medium: A Practical Guide to XCFrameworks
- XCFrameworks vs Universal Frameworks: The New Standard for Code Distribution
- Phillip Jacobs: Create-XCFramework
- GitHub Gist: Example Script for XCFramework Creation
- Stack Overflow: Common Issues with XCFrameworks
- Medium: Resolving XCFramework Issues in Xcode
- WWDC 2019: Binary Frameworks in Swift
- WWDC 2020: Advances in Build System
- Swift by Sundell: Packaging Frameworks
- Reddit: r/iOSProgramming - Discussions on XCFrameworks
- Strip framework
Share on: