简体   繁体   English

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

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

In Objective-C it was sometimes useful to use static string constants to define alternate API keys (for example to differentiate between RELEASE and DEBUG keys for analytics packages, like MixPanel, Flurry or Crashlytics):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

and then...接着...

[Analytics startSession:API_KEY];

How does this translate to Swift, since the Swift compiler no longer uses a preprocessor?这如何转化为 Swift,因为 Swift 编译器不再使用预处理器?

Apple included full support for Swift preprocessor flags as of Xcode 8 , so it's no longer necessary to set these values in "Other Swift Flags". Apple 从Xcode 8开始全面支持 Swift 预处理器标志,因此不再需要在“Other Swift Flags”中设置这些值。

The new setting is called "Active Compilation Conditions", which provides top-level support for the Swift equivalent of preprocessor flags.新设置称为“活动编译条件”,它为 Swift 等效的预处理器标志提供顶级支持。 You use it in exactly the same way as you would "Other Swift Flags", except there's no need to prepend the value with a "-D" (so it's just a little cleaner).使用它的方式与使用“Other Swift Flags”完全相同,只是不需要在值前加上“-D”(所以它更简洁一些)。

From the Xcode 8 release notes :来自Xcode 8 发行说明

Active Compilation Conditions is a new build setting for passing conditional compilation flags to the Swift compiler. Active Compilation Conditions是一种新的构建设置,用于将条件编译标志传递给 Swift 编译器。 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.此设置值的每个元素都以-D为前缀传递给 swiftc,就像Preprocessor Macros的元素以相同的前缀传递给 clang。 (22457329) (22457329)

在此处输入图像描述

You use the above setting like so:您可以像这样使用上述设置:

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

UPDATED: Xcode 8 now supports this automatically, see @dwlz's response above.更新:Xcode 8 现在自动支持此功能,请参阅上面@dwlz 的回复。

Prior to Xcode 8, you could still use Macros in the same way:在 Xcode 8 之前,您仍然可以以相同的方式使用宏:

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

However in order for them to be picked up by Swift, you need to set "Other Swift Flags" in your target's Build Settings:但是,为了让 Swift 获取它们,您需要在目标的 Build Settings 中设置“Other Swift Flags”:

  • Open Build Settings for your target为您的目标打开构建设置
  • Search for "other swift flags"搜索“其他快速标志”
  • Add the macros you wish to use, preceded by the -D flag添加您希望使用的宏,前面-D标志

在此处输入图像描述

As a follow up observation, try not to keep api keys / secrets in plaintext in the repository.作为后续观察,尽量不要将 api 密钥/秘密以明文形式保存在存储库中。 Use a secrets management system to load the keys / secrets into the user's environment variables.使用秘密管理系统将密钥/秘密加载到用户的环境变量中。 Otherwise step 1 is necessary, if acceptable.否则,如果可以接受,则必须执行第 1 步。

  1. Put the "secrets" in a plaintext file above in the enclosing repository将“秘密”放在上面封闭存储库中的纯文本文件中
  2. Create a ../set_keys.sh that contains a list of export API_KEY_A='<plaintext_key_aef94c5l6>' (use single quote to prevent evaluation)创建一个包含导出../set_keys.sh export API_KEY_A='<plaintext_key_aef94c5l6>'列表的 ../set_keys.sh (使用单引号来防止评估)
  3. Add a Run script phase that can source ../set_keys.sh and move it to the top of execution order添加一个运行脚本阶段,该阶段可以source ../set_keys.sh并将其移动到执行顺序的顶部
  4. In Build Settings > Preprocessor Macros, add to defines as necessary such as API_KEY_A="$API_KEY_A"在 Build Settings > Preprocessor Macros 中,根据需要添加定义,例如API_KEY_A="$API_KEY_A"

That captures the environment variable into the compiler define which is later used in each clang invocation for each source file.这会将环境变量捕获到编译器定义中,稍后在每个源文件的每个 clang 调用中使用该环境变量。

Example directory structure示例目录结构

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

In swift packages you have to do this inside of the swiftSettings argument to .target in your Package.swift file.在 swift 包中,您必须在Package.swift文件中的.targetswiftSettings参数中执行此操作。 Use the define method (Apple documentation) or Swift documentation使用define方法(Apple 文档)Swift 文档

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

Mine looks like this and it works!我的看起来像这样,它的工作原理!

            swiftSettings: [
               .define("VAPOR")
            ]

in my code I can conditionally compile using this:在我的代码中,我可以有条件地使用它进行编译:

#if VAPOR

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM