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

Swift 5.9宏中如何获取应用Bundle.main或读取info.plist?

在Swift 5.9宏中读取应用Info.plist的解决方案

为什么直接用Bundle API行不通

宏是在编译阶段的独立进程中执行的,和目标应用的运行环境完全隔离:

  • 调用Bundle.main拿到的是宏自身所在的工具包bundle,而非目标应用的bundle
  • 通过bundle标识符查找时,目标应用的bundle还未构建完成,自然返回nil

下面提供几种可行的解决思路:


1. 直接读取Info.plist文件路径

利用宏的MacroExpansionContext获取目标模块的编译上下文,定位到Info.plist的物理文件路径,再直接解析该文件:

import SwiftSyntax
import SwiftSyntaxMacros
import Foundation

struct ReadInfoPlistMacro: ExpressionMacro {
    static func expansion(
        of node: some FreestandingMacroExpansionSyntax,
        in context: some MacroExpansionContext
    ) throws -> ExprSyntax {
        // 从编译参数中获取目标模块的Info.plist路径
        guard let infoPlistPath = context.swiftCompilerArguments.first(where: { $0.starts(with: "-info-plist") })?.split(separator: " ").last else {
            throw MacroError(message: "无法找到Info.plist路径")
        }
        
        // 解析Info.plist文件
        let url = URL(fileURLWithPath: String(infoPlistPath))
        guard let data = try? Data(contentsOf: url),
              let plist = try? PropertyListSerialization.propertyList(from: data, format: nil) as? [String: Any] else {
            throw MacroError(message: "解析Info.plist失败")
        }
        
        // 读取指定配置项(比如关联XCConfig的属性)
        guard let customValue = plist["MY_CUSTOM_KEY"] as? String else {
            throw MacroError(message: "Info.plist中未找到MY_CUSTOM_KEY配置")
        }
        
        // 返回验证后的结果(这里示例返回字符串字面量)
        return "\"\(customValue)\""
    }
}

enum MacroError: Error {
    case message(String)
}

注意:-info-plist是Xcode编译时自动传入的参数,指向目标的Info.plist文件路径,通过这个参数可以直接定位到文件。


2. 直接传递XCConfig配置为编译常量

既然你的Info.plist配置关联了XCConfig,更高效的方式是将XCConfig中的值直接作为编译定义常量传入,宏可以直接读取这些常量,无需解析Info.plist:

  1. 在你的XCConfig文件中添加:
MY_CUSTOM_CONFIG = $(MY_XC_CONFIG_KEY)
OTHER_SWIFT_FLAGS = $(inherited) -D MY_CUSTOM_CONFIG=\"$(MY_CUSTOM_CONFIG)\"
  1. 在宏中直接读取该常量:
struct ValidateConfigMacro: ExpressionMacro {
    static func expansion(
        of node: some FreestandingMacroExpansionSyntax,
        in context: some MacroExpansionContext
    ) throws -> ExprSyntax {
        #if MY_CUSTOM_CONFIG
            // 验证配置值
            guard MY_CUSTOM_CONFIG.count > 0 else {
                throw MacroError(message: "MY_CUSTOM_CONFIG配置不能为空")
            }
            return "\"\(MY_CUSTOM_CONFIG)\""
        #else
            throw MacroError(message: "未传入MY_CUSTOM_CONFIG编译常量")
        #endif
    }
}

这种方式避免了文件IO,编译阶段直接获取配置,效率更高。


3. 使用Swift Compiler Plugin增强能力

如果需要更复杂的编译上下文信息,可以使用Swift Compiler Plugin(需Swift 5.9+),它能获取目标的完整编译设置、文件列表等信息,进而定位和解析Info.plist:

import SwiftCompilerPlugin
import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros

@main
struct MyPlugin: CompilerPlugin {
    let providingMacros: [Macro.Type] = [
        ReadInfoPlistMacro.self
    ]
}

// 宏实现可以通过PluginContext获取更多编译信息
extension ReadInfoPlistMacro {
    static func expansion(
        of node: some FreestandingMacroExpansionSyntax,
        in context: some MacroExpansionContext
    ) throws -> ExprSyntax {
        // 如果是PluginContext,可以获取目标模块的更多路径信息
        if let pluginContext = context as? PluginContext {
            let targetDir = pluginContext.targetDirectory
            let infoPlistUrl = targetDir.appendingPathComponent("Info.plist")
            // 后续解析逻辑同方法1
        }
        // ...
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 01:31:21