Creating XCFramework from SPM package

documentationas Apple recommends to create a binary artifact.

To begin, you need to set the following values ​​for environment variables in the project settings:

  • SKIP_INSTALL=NO, for the product to be included in the archive,

  • BUILD_LIBRARY_FOR_DISTRIBUTION=YES, so that the product interface is generated.

And run the command to create the archive:

xcodebuild archive
    -project MyFramework.xcodeproj
    -scheme MyFramework
    -destination "generic/platform=iOS"
    -archivePath "archives/MyFramework"

Immediately after it is the xcframework build command:

xcodebuild -create-xcframework
    -archive archives/MyFramework-iOS.xcarchive -framework MyFramework.framework
    -archive archives/MyFramework-iOS_Simulator.xcarchive -framework MyFramework.framework
    -archive archives/MyFramework-macOS.xcarchive -framework MyFramework.framework
    -archive archives/MyFramework-Mac_Catalyst.xcarchive -framework MyFramework.framework
    -output xcframeworks/MyFramework.xcframework

Examples from the documentation.

Let's try to do the same for the SPM package. Let's create a new package MyFramework. Specify the library type .dynamic, because it is necessary for creation .framework.

Package.swift

let package = Package(
    name: "MyFramework",
    products: [
        .library(
            name: "MyFramework",
            type: .dynamic,
            targets: ["MyFramework"]),
    ],
    dependencies: [
    ],
    targets: [
        .target(
            name: "MyFramework",
            dependencies: []),
        .testTarget(
            name: "MyFrameworkTests",
            dependencies: ["MyFramework"]),
    ]
)

Let's call the creation command xcarchive:

xcodebuild archive
    -scheme MyFramework
    -destination "generic/platform=iOS"
    -archivePath "archives/MyFramework.xcarchive"
    SKIP_INSTALL=NO
    BUILD_LIBRARY_FOR_DISTRIBUTION=YES

And then the creation command xcframework:

xcodebuild -create-xcframework
    -archive "archives/MyFramework.xcarchive"
    -framework MyFramework.framework
    -output xcframeworks/MyFramework.xcframework

As a result of executing the last command, we get the following error:

error: the path does not point to a valid framework: /archives/MyFramework.xcarchive/Products/Library/Frameworks/MyFramework.framework

This error means that there is no path on the searched path. MyFramework.framework. Let's try to figure it out. To do this, let's look at what's inside the created MyFramework.xcarchive:

And inside MyFramework.framework there really is no path along the desired path, the error is quite logical.

In the official documentation, the creation of a binary artifact is based on the project .xcodeproj, where all the values ​​of the environment variables that the build depends on are already set. Let's analyze all the environment variables and set the necessary values. Their description can be found at the links: once, two.

First, we need to change the installation path .framework. To do this, you need to specify the directory where the product is placed inside the archive in the environment variable INSTALL_PATH:

INSTALL_PATH=/Library/Frameworks

As a result .framework is located where the build team expects it xcframework, and, as a result, the binary artifact is successfully created:

xcframework successfully written out to: /xcframework/MyFramework.xcframework

But when using it we get the following error:

In simple terms, this error means that consumers of this xcframework can't understand the interface.

To fix the situation, we set the product type and the directory where the interface will be placed in the following environment variables, respectively:

PRODUCT_TYPE=com.apple.product-type.framework MODULES_FOLDER_PATH=MyFramework.framework/Modules

As a result, the archive contains .swiftmodule. When using xcframework, created on the basis of such an archive, there are no more errors.

We will also indicate the need to add the header file to the archive, as well as the directory in which this file will be located, to the following environment variables, respectively:

SWIFT_INSTALL_OBJC_HEADER=YESPUBLIC_HEADERS_FOLDER_PATH=MyFramework.framework/Headers

The resulting command to create an archive looks like this:

xcodebuild archive
    -scheme MyFramework
    -destination "generic/platform=iOS"
    -archivePath "archives/MyFramework.xcarchive"
    SKIP_INSTALL=NO
    BUILD_LIBRARY_FOR_DISTRIBUTION=YES
    INSTALL_PATH=/Library/Frameworks
    MODULES_FOLDER_PATH=MyFramework.framework/Modules
    PRODUCT_TYPE=com.apple.product-type.framework
    PUBLIC_HEADERS_FOLDER_PATH=MyFramework.framework/Headers
    SWIFT_INSTALL_OBJC_HEADER=YES

So, creating an XCFramework from an SPM package is essentially no different from creating it from xcodeproj. However, we need to dive deeper to understand how to do this. For example, here A large script has been implemented to perform this task.

Before coming up with your own solution, it is better to try to understand the existing tools. If you want to dig into this topic, you can try to build XCFramework from the SPM package with resources.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *