You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

带SPM依赖的XCFramework存在依赖暴露异常与重复定义问题

问题背景

我有一个作为Swift包管理器(SPM)包的库ExampleLibrary,它依赖第三方库CryptoSwift,其Package.swift配置如下:

// swift-tools-version: 5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "ExampleLibrary",
    platforms: [
            .macOS(.v10_15), .iOS(.v13), .tvOS(.v13)
        ],
    products: [
        // Products define the executables and libraries a package produces, making them visible to other packages.
        .library(
            name: "ExampleLibrary",
            type: .dynamic,
            targets: ["ExampleLibrary"]),
    ],
    dependencies: [
        .package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMajor(from: "1.8.1")),
    ],
    targets: [
        // Targets are the basic building blocks of a package, defining a module or a test suite.
        // Targets can depend on other targets in this package and products from dependencies.
        .target(
            name: "ExampleLibrary",
            dependencies: ["CryptoSwift"]),
        .testTarget(
            name: "ExampleLibraryTests",
            dependencies: ["ExampleLibrary"]),
    ]
)

将该库编译导出为二进制XCFramework后,我将其作为另一个SPM包ExampleSDK进行分发,配置如下:

// swift-tools-version: 5.9

import PackageDescription

let package = Package(
    name: "ExampleSDK",
    platforms: [
        .iOS(.v13),
        .macOS(.v10_15),
    ],
    products: [
        .library(
            name: "ExampleSDK",
            targets: ["ExampleLibraryFramework"]),
    ],
    targets: [
        .binaryTarget(name: "ExampleLibraryFramework", path: "ExampleLibrary.xcframework")
    ]
)

当我将ExampleSDK导入到App项目中并使用:

import ExampleLibrary

此时会报错:Missing required module 'CryptoSwift'。

若我在App项目中单独导入CryptoSwift作为依赖,则会出现重复定义错误:

objc[5702]: Class _TtC11CryptoSwift15XChaCha20Worker is implemented in both [...]/ExampleLibrary (0x1016edcf0) and [...]/example.app/example (0x100611908). One of the two will be used. Which one is undefined.

即使我在ExampleSDK的配置中添加CryptoSwift依赖(如下所示),虽然不再提示缺失依赖,但重复定义问题依然存在:

// swift-tools-version: 5.9

import PackageDescription

let package = Package(
    name: "ExampleSDK",
    platforms: [
        .iOS(.v13),
        .macOS(.v10_15),
    ],
    products: [
        .library(
            name: "ExampleSDK",
            targets: ["ExampleLibraryFramework", "ExampleSDK"]),
    ],
    dependencies: [
        .package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMajor(from: "1.8.1")),
    ],
    targets: [
        .target(name: "ExampleSDK", dependencies: ["CryptoSwift"]),
        .binaryTarget(name: "ExampleLibraryFramework", path: "ExampleLibrary.xcframework")
    ]
)

我尝试过在ExampleLibrary中使用@_implementationOnly import CryptoSwift来隐藏依赖,但无法阻止App导入CryptoSwift时出现重复问题。

现提出两个问题:

  1. 如何阻止ExampleLibrary暴露CryptoSwift,使App导入时不会出现重复定义问题?
  2. 如何让App项目识别到该依赖已嵌入在XCFramework中?

(XCFramework内部结构:XCFramework文件夹结构)

解决方案

问题1:阻止ExampleLibrary暴露CryptoSwift

要彻底隐藏CryptoSwift依赖,需从编译阶段和包配置双管齐下:

  • 规范@_implementationOnly使用并清理公开接口
    在ExampleLibrary的所有源文件中,将普通import CryptoSwift替换为@_implementationOnly import CryptoSwift。同时检查库的公开接口文件(.swiftinterface),确保没有任何CryptoSwift的类型、协议或符号被暴露——如果库的公开API直接使用了CryptoSwift的类型(比如函数参数/返回值),必须封装成自身库的内部类型,避免对外透传第三方依赖。
  • 编译XCFramework时静态链接CryptoSwift
    修改ExampleLibrary的Package.swift,强制静态链接CryptoSwift,让第三方库的符号直接嵌入到ExampleLibrary的二进制中,而非作为动态依赖存在:
    .target(
        name: "ExampleLibrary",
        dependencies: [
            .product(name: "CryptoSwift", package: "CryptoSwift", type: .static)
        ]
    )
    
    之后使用swift package create-xcframework命令编译XCFramework,确保构建过程中依赖被静态嵌入。

问题2:让App项目识别依赖已嵌入XCFramework

需在ExampleSDK的Package.swift中正确配置二进制目标的链接规则,告诉SPM无需额外引入CryptoSwift:

// swift-tools-version: 5.9
import PackageDescription

let package = Package(
    name: "ExampleSDK",
    platforms: [
        .iOS(.v13),
        .macOS(.v10_15),
    ],
    products: [
        .library(
            name: "ExampleSDK",
            targets: ["ExampleLibraryFramework"]),
    ],
    targets: [
        .binaryTarget(
            name: "ExampleLibraryFramework",
            path: "ExampleLibrary.xcframework",
            linkerSettings: [
                // 告知链接器无需额外链接CryptoSwift
                .linkedLibrary("CryptoSwift", .when(platforms: [.iOS, .macOS])),
                // 避免因未找到外部依赖报错
                .unsafeFlags(["-Xlinker", "-undefined", "dynamic_lookup"])
            ],
            // 标记该二进制目标不对外暴露依赖
            excludeDependencies: ["CryptoSwift"]
        )
    ]
)

同时可以通过以下方式验证:

  • 检查XCFramework中各平台二进制文件的Info.plist,确认没有包含CryptoSwift的依赖信息;
  • 使用otool -L命令查看二进制文件,确认CryptoSwift的符号已被静态嵌入,而非动态依赖项。

额外注意事项

  • 如果CryptoSwift本身默认是动态库,需要先将其编译为静态库再链接,可通过修改CryptoSwift的Package.swift或构建时传递--static-swift-stdlib参数实现;
  • 确保App项目不会单独引入CryptoSwift,依赖的唯一来源应为ExampleLibrary的XCFramework。

内容的提问来源于Stack Exchange,提问作者Paweł Madej

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 11:19:54