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

能否通过Swift代码列出所有SPM依赖?如何实现?

存在可行方案,没有零配置的通用纯Swift实现,但只需要极少量项目配置,就能稳定拿到全量SPM依赖的完整信息,分两种常用场景给实现路径:

方案1:运行时读取(适合App内展示依赖清单、合规校验场景)

这是实现成本最低、稳定性最高的方案,核心是利用SPM自动维护的Package.resolved依赖锁文件做解析。

实现步骤

  • 打开Xcode项目,找到对应App target的Build Phases标签,进入Copy Bundle Resources阶段,点击+号把项目根目录下的Package.resolved文件加入资源列表,不要勾选Copy only when installing选项。
  • 新增解析代码,Package.resolved是标准JSON格式,直接用Codable解码即可,代码示例如下:
// 定义和Package.resolved结构匹配的数据模型
struct ResolvedPackage: Codable {
    let pins: [Pin]
}

struct Pin: Codable {
    let identity: String
    let location: String
    let state: PinState
}

struct PinState: Codable {
    let revision: String?
    let version: String?
    let branch: String?
}

// 依赖加载方法
func loadAllSPMDependencies() throws -> [Pin] {
    guard let resURL = Bundle.main.url(forResource: "Package", withExtension: "resolved") else {
        throw NSError(domain: "SPMDepError", code: -1, userInfo: [NSLocalizedDescriptionKey: "未找到锁文件,请确认Package.resolved已加入Copy Bundle Resources阶段"])
    }
    let rawData = try Data(contentsOf: resURL)
    return try JSONDecoder().decode(ResolvedPackage.self, from: rawData).pins
}

适配说明:Xcode 15及以上版本生成的新版Package.resolved根节点直接为pins数组,上述模型可直接使用;如果是Xcode 14及更早版本生成的旧格式锁文件,根节点为object.pins,对应调整解码层级即可。

该方案的优势是一次配置后无需额外维护,新增、删除、升级SPM依赖时Package.resolved会自动更新,解析结果包含依赖名、仓库地址、版本号、提交哈希全量信息;仅有的注意点是如果你的项目有多Configuration配置,要确认所有Configuration下Package.resolved都会被正确打包进Bundle。

方案2:构建阶段自动生成(适合CI校验、不想额外带资源文件的场景)

如果不想把Package.resolved作为资源打包进App,可以加一个预编译Swift脚本,在每次构建时自动生成依赖清单的Swift代码,直接编译进二进制。

实现步骤

  • 打开Xcode项目,找到对应target的Build Phases标签,新增一个Run Script阶段,拖到Compile Sources阶段之前,Shell路径填/usr/bin/swift。
  • 在脚本框中写入Swift解析代码,读取项目根目录的Package.resolved,生成固定格式的Swift源码文件到项目源码目录,最简脚本示例如下:
#!/usr/bin/env swift
import Foundation

// 这里补全方案1里的ResolvedPackage、Pin、PinState结构体定义
let projectRoot = ProcessInfo.processInfo.environment["PROJECT_DIR"]!
let resolvedFileURL = URL(fileURLWithPath: projectRoot).appendingPathComponent("Package.resolved")
let outputFileURL = URL(fileURLWithPath: projectRoot).appendingPathComponent("你的项目源码目录/SPMDepList.generated.swift")

let rawData = try Data(contentsOf: resolvedFileURL)
let depList = try JSONDecoder().decode(ResolvedPackage.self, from: rawData).pins

var generatedCode = """
struct SPMDep {
    let name: String
    let repo: String
    let version: String?
    let commit: String?
}
let allSPMDeps: [SPMDep] = [
"""
depList.forEach { dep in
    let verStr = dep.state.version == nil ? "nil" : "\"\(dep.state.version!)\""
    let commitStr = dep.state.revision == nil ? "nil" : "\"\(dep.state.revision!)\""
    generatedCode += "    SPMDep(name: \"\(dep.identity)\", repo: \"\(dep.location)\", version: \(verStr), commit: \(commitStr)),\n"
}
generatedCode += "]\n"
try generatedCode.write(to: outputFileURL, atomically: true, encoding: .utf8)
  • 首次执行脚本生成SPMDepList.generated.swift文件后,把该文件拖入项目加入编译target即可,后续构建时脚本会自动更新文件内容,不需要手动维护。
不推荐的方案说明

不要尝试通过遍历dyld加载镜像列表的方式枚举SPM依赖:这种方式只能拿到动态链接的库名,拿不到版本、仓库地址等核心信息,且静态链接的SPM依赖不会出现在dyld列表中,漏报率极高,没有实用价值。

内容的提问来源于stack exchange,提问作者Arainty

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 21:39:29