14

我有三个API不同的API Keys和一些不同的设置

  • 用于开发或内部测试构建- iOS App Store 之外的开发分发

    • Host-devapi.project-name.com
    • API Key- development_key
    • FLEX[ 1 ] - 启用
  • 用于客户端测试构建- iOS App Store 之外的企业分发

    • Host- stgapi.project-name.com
    • API Key- 企业密钥
    • FLEX- 使能够
  • 用于生产构建- 在 iOS App Store 中分发

    • Host- API.project-name.com
    • API key- app_store_key
    • FLEX- 禁用

我可以通过使用来管理两个设置DEBUG

#if DEBUG
    #define API_BASE_URL @"http://devapi.project-name.com/api/v1"
    #define API_KEY @"development_key"
#else
    #define API_BASE_URL @"http://stgapi.project-name.com/api/v1"
    #define API_KEY @"enterprise_key"
#endif

// In AppDelegate.m 
#if DEBUG
    [[FLEXManager sharedManager] showExplorer];
#endif

但是第一个问题是企业分发(用于客户端测试)和 iOS App Store 分发(生产)构建,对于企业和 App Store 分发每次都需要更改代码

  • 对于企业分布

    #if DEBUG
        //debug setting
    #else
        //enterprise setting
        #define API_BASE_URL @"http://stgapi.project-name.com/api/v1"
        #define API_KEY @"enterprise_key"
    #endif
    
  • 用于 App Store 分发

    #if DEBUG
        //debug setting
    #else
        //app store setting
        #define API_BASE_URL @"http://api.project-name.com/api/v1"
        #define API_KEY @"app_store_key"
    #endif
    

我正在寻找这样的方式

#ifdef DEVELOPMENT
    #define API_BASE_URL @"http://devapi.project-name.com/api/v1"
    #define API_KEY @"development_key"
#elif ENTERPRISE
    #define API_BASE_URL @"http://stgapi.project-name.com/api/v1"
    #define API_KEY @"enterprise_key"
#elif APP_STORE
    #define API_BASE_URL @"http://api.project-name.com/api/v1"
    #define API_KEY @"app_store_key"
#endif

还是其他?


第二个问题

有没有办法在不创建不同目标的情况下创建三个具有不同名称的构建?

  • ProductName- 对于应用商店
  • ProductName-Dev- 用于内部开发构建
  • ProductName-Stg- 用于客户端测试(企业)构建

我刚刚根据iamnichols提供的解决方案创建了演示项目和完整的视觉指南

4

3 回答 3

16

调试和发布版本之间的区别在于,一个是存档和导出的,而另一个是通过调试器中的 Xcode 在本地运行的。您可能会发现有时您也想在调试器中运行生产或暂存构建,但是通过将内容拆分出来#ifdef DEBUG,您可能会遇到问题。

这是我所做的简化版本:

创建单独的配置

在项目(非目标)设置中,创建(从原件复制)以下配置:

  • Debug_Dev
  • Debug_Staging
  • Debug_Prod
  • Release_Dev
  • Release_Staging
  • Release_Prod

请注意,如果您使用 Cocoapods,则需要将配置设置回 none,删除项目(不是 Pods 项目)中 Pods 文件夹的内容并重新运行pod install

为每个环境创建一个方案

而不是只有一个 MyApp 方案,创建以下(复制原始):

  • MyApp_Dev
  • MyApp_Staging
  • MyApp_Prod

在每个方案中,在适当的情况下使用关联的 Debug_* 和 Release_* 配置。

添加预处理器宏以识别环境

添加一个额外的预处理器宏来识别您正在构建的环境。

在项目构建设置中,单击 + 并添加用户定义的构建设置并将其命名为MYAPP_ENVIRONMENT. 然后,对于每组不同的环境,为每个环境添加不同的预处理器宏。即和。ENV_DEV=1_ENV_STAGING=1ENV_PROD=1

然后,在 c 预处理器宏中(同样在项目级别而不是目标级别)使用$(MYAPP_ENVIRONMENT).

这样,您就可以像这样确定要构建的环境:

#ifdef ENV_DEV
    NSString * const MyAppAPIBaseURL = @"https://api-dev.myapp.com/";
#elif ENV_SAGING
    NSString * const MyAppAPIBaseURL = @"https://api-staging.myapp.com/";
#elif ENV_PROD
    NSString * const MyAppAPIBaseURL = @"https://api.myapp.com/";
#endif

可能需要接受很多,但请告诉我你的进展情况。


然后,您还可以创建不同的用户定义的构建设置来执行不同的操作,例如更改应用程序的显示名称。

您可以通过创建一个新设置来做到这一点MYAPP_DISPLAY_NAME,例如,为每个配置设置正确的名称,然后在您的info.plist设置中将 Bundle Display Name 的值设置为$(MYAPP_DISPLAY_NAME).

于 2016-02-23T08:25:05.960 回答
1

细节

  • Xcode 版本 10.2.1 (10E1001),Swift 5

解决方案

1) 创建(或复制)目标

在此处输入图像描述

或者

在此处输入图像描述

我的样本:

我复制了现有的目标并重命名了它们。我的目标名称:

  • 应用制作
  • 应用登台
  • 应用开发

2) 组织和重命名信息列表

我将所有 plists 放在一个文件夹中:info.plists

在此处输入图像描述

3) 重命名构建方案

在此处输入图像描述


在此处输入图像描述

4)检查构建方案参数

按下编辑按钮

在此处输入图像描述


检查您的构建方案是否连接到正确的目标。

我的样本:

应用开发构建方案(看下图左上角)连接到应用开发目标(目标放置在下图的中心)。

  • App-Development 构建方案中选择的目标是App-Development
  • App-Staging 构建方案中选择的目标是App-Staging
  • App-Production 构建方案中选择的目标是App-Production

在此处输入图像描述


还要检查可执行应用程序。

我的样本:

  • App-Development 构建方案中,可执行应用程序是App-Development.app
  • App-Staging 构建方案中,可执行应用程序是App-Staging.app
  • App-Production 构建方案中,可执行应用程序是App-Production.app

在此处输入图像描述

5) 向信息列表中添加值

在此处输入图像描述

我的样本:

信息生产.plist

<key>LSEnvironment</key>
<dict>
    <key>Environment</key>
    <string>Production</string>
    <key>Host</key>
    <string>https://production.host.com</string>
    <key>AppID</key>
    <integer>1</integer>
    <key>AdvertisementEnabled</key>
    <true/>
</dict>

信息开发.plist

<key>LSEnvironment</key>
<dict>
    <key>Environment</key>
    <string>Development</string>
    <key>Host</key>
    <string>https://development.host.com</string>
    <key>AppID</key>
    <integer>2</integer>
    <key>AdvertisementEnabled</key>
    <false/>
</dict>

Info-Staging.plist

<key>LSEnvironment</key>
<dict>
    <key>Environment</key>
    <string>Staging</string>
    <key>Host</key>
    <string>https://staging.host.com</string>
    <key>AppID</key>
    <integer>3</integer>
    <key>AdvertisementEnabled</key>
    <false/>
</dict>

环境.swift

import Foundation

// MARK: - Environment main class

class Environment {

    class Value { private init(){} }
    class Enums { private init(){} }
}

extension Environment.Value {
    static var all: [String: Any] {
        return Bundle.main.infoDictionary?["LSEnvironment"] as? [String: Any] ?? [:]
    }
}

extension Environment.Value {

    private enum Keys: String {
        case environment = "Environment"
        case host = "Host"
        case appID = "AppID"
        case advertisementEnabled = "AdvertisementEnabled"
    }

    private static func get<T>(value key: Keys, type: T.Type) -> T? {
        return all[key.rawValue] as? T
    }
}

// MARK: - Environment type value

extension Environment.Enums {
    enum EnvironmentType: String {
        case production = "Production"
        case staging = "Staging"
        case development = "Development"
    }
}

extension Environment.Value {
    static var type: Environment.Enums.EnvironmentType {
        let environment = get(value: .environment, type: String.self)!
        return Environment.Enums.EnvironmentType(rawValue: environment)!
    }
}

// MARK: - Host (sample with string)

extension Environment.Value {
    static var host: String { return get(value: .host, type: String.self)! }
}

// MARK: - App ID (sample with number)

extension Environment.Value {
    static var appID: Int { return get(value: .appID, type: Int.self)! }
}

// MARK: - Advertisement Enabled (sample with bool)

extension Environment.Value {
    static var advertisementEnabled: Bool { return get(value: .advertisementEnabled, type: Bool.self)! }
}

用法

print("All values: \(Environment.Value.all)")

switch Environment.Value.type {
    case .development: print("Environment: dev")
    case .staging: print("Environment: stage")
    case .production: print("Environment: prod")
}
print("Host: \(Environment.Value.host)")
print("App ID: \(Environment.Value.appID)")
print("Advertisement Enabled: \(Environment.Value.advertisementEnabled)")

在此处输入图像描述

当您将运行您的构建方案之一时,您将拥有不同的值。

选择构建方案

在此处输入图像描述

在此处输入图像描述

于 2019-06-30T21:40:07.273 回答
0

一个更简单且不太复杂的解决方案是为每个配置使用不同的头文件,并且只导入其中一个。这不是自动的,但相当简单:

// You only need to switch the following lines when passing from qa 2 production and back:
#import "Mode_QA.h"
//#import "Mode_Production.h"
于 2016-02-23T08:41:18.807 回答