Swift:如何使用 PREPROCESSOR 标志(如 `#if DEBUG`)来实现 API 键?

Swift: how to use PREPROCESSOR Flags (like `#if DEBUG`) to implement API keys?

Objective-C 中,有时使用静态字符串常量来定义备用 API 键很有用(例如,区分分析包的 RELEASE 和 DEBUG 键,如 MixPanel、Flurry 或 Crashlytics) :

#if DEBUG
static NSString *const API_KEY = @"KEY_A";
#else
static NSString *const API_KEY = @"KEY_B";
#endif

然后...

[Analytics startSession:API_KEY];

这如何转化为 Swift,因为 Swift 编译器不再使用预处理器?

更新:Xcode 8 现在自动支持此功能,请参阅上面@DanLoewenherz 的回复。

在 Xcode8 之前,您仍然可以以相同的方式使用宏:

#if DEBUG
let apiKey = "KEY_A"
#else
let apiKey = "KEY_B"
#endif

然而,为了让它们被 Swift 拾取,您需要在目标的构建设置中设置 "Other Swift Flags":

  • 打开目标的构建设置
  • 搜索 "other swift flags"
  • 添加您希望使用的宏,前面加上 -D 标志

Apple 从 Xcode 8 开始全面支持 Swift 预处理器标志,因此不再需要在“其他 Swift 标志”中设置这些值。

新设置称为“活动编译条件”,它为预处理器标志的 Swift 等价物提供顶级支持。您使用它的方式与使用“其他 Swift 标志”的方式完全相同,只是不需要在值前加上“-D”(因此它更简洁)。

来自Xcode 8 release notes

Active Compilation Conditions is a new build setting for passing conditional compilation flags to the Swift compiler. Each element of the value of this setting passes to swiftc prefixed with -D, in the same way that elements of Preprocessor Macros pass to clang with the same prefix. (22457329)

你像这样使用上面的设置:

#if DEBUG
    let accessToken = "DebugAccessToken"
#else
    let accessToken = "ProductionAccessToken"
#endif

作为后续观察,尽量不要在存储库中以明文形式保存 api 密钥/秘密。使用机密管理系统将密钥/机密加载到用户的环境变量中。否则,如果可以接受,第 1 步是必要的。

  1. 将 "secrets" 放在上面的封闭存储库中的纯文本文件中
  2. 创建一个包含 export API_KEY_A='<plaintext_key_aef94c5l6>' 列表的 ../set_keys.sh(使用单引号防止求值)
  3. 添加一个运行脚本阶段,可以source ../set_keys.sh并将其移动到执行顺序的顶部
  4. 在 Build Settings > Preprocessor Macros 中,根据需要添加定义,例如 API_KEY_A="$API_KEY_A"

将环境变量捕获到编译器定义中,稍后在每个源文件的每个 clang 调用中使用。

示例目录结构

[10:33:15] ~/code/memo yes? tree -L 2 .
.
├── Memo
│   ├── Memo
│   ├── Memo.xcodeproj
│   ├── Memo.xcworkspace
│   ├── Podfile
│   ├── Podfile.lock
│   └── Pods
└── keys

在 swift 包中,您必须在 Package.swift 文件中 .targetswiftSettings 参数内执行此操作。使用define方法(Apple documentation) or Swift documentation

targets: [
.target(name: String,
            dependencies: [Target.Dependency],
            path: String?,
            exclude: [String]?,
            sources: [String]?,,
            cSettings: [CSetting]?,
            cxxSettings: [CXXSetting]?,
            swiftSettings: [SwiftSetting]?,
            linkerSettings: [LinkerSetting]?),

我的看起来像这样,而且有效!

            swiftSettings: [
               .define("VAPOR")
            ]

在我的代码中,我可以使用这个有条件地编译:

#if VAPOR