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:
- 在你的XCConfig文件中添加:
MY_CUSTOM_CONFIG = $(MY_XC_CONFIG_KEY) OTHER_SWIFT_FLAGS = $(inherited) -D MY_CUSTOM_CONFIG=\"$(MY_CUSTOM_CONFIG)\"
- 在宏中直接读取该常量:
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
相关产品推荐
相关产品推荐

