【问题标题】:Best way to Manage Development, Testing, and Production builds with different settings and name使用不同设置和名称管理开发、测试和生产构建的最佳方式
【发布时间】:2016-06-04 22:20:23
【问题描述】:

我有三个 APIAPI 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 分发(生产)构建,适用于企业和应用程序 每次店铺分发都需要改代码

  • 用于企业分发

    #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提供的解决方案创建了演示项目和完整的视觉指南

【问题讨论】:

    标签: ios objective-c xcode xcode-scheme xcode-configuration


    【解决方案1】:

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

    这是我所做工作的简化版本:

    创建个人配置

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

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

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

    为每个环境创建一个方案

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

    • MyApp_Dev
    • MyApp_Staging
    • MyApp_Prod

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

    添加预处理宏来识别环境

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

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

    然后,在 c 预处理器宏中(同样是在项目级别而不是目标级别)使用 $(MYAPP_ENVIRONMENT) 添加这个新的 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 中将捆绑显示名称的值设置为 $(MYAPP_DISPLAY_NAME)

    【讨论】:

    • 完美答案!!谢谢!!
    【解决方案2】:

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

    // You only need to switch the following lines when passing from qa 2 production and back:
    #import "Mode_QA.h"
    //#import "Mode_Production.h"
    

    【讨论】:

      【解决方案3】:

      详情

      • 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 build scheme 可执行应用程序是 App-Development.app
      • App-Staging 构建方案 可执行应用程序是 App-Staging.app
      • App-Production 构建方案 中,可执行应用程序是 App-Production.app

      5) 向信息列表添加值

      我的样本:

      Info-Production.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>
      

      Info-Development.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>
      

      Environment.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)")
      

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

      选择构建方案

      运行

      【讨论】:

        猜你喜欢
        • 2021-10-08
        • 2012-01-05
        • 2016-04-07
        • 2012-05-26
        • 2016-10-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多