程序调试 (五) —— 使用Build Configurations 和 .xcconfig 构建你的App(一)

版本记录

版本号 时间
V1.0 2021.06.09 星期三

前言

程序总会有bug,如果有好的调试技巧和方法,那么就是事半功倍,这个专题专门和大家分享下和调试相关的技巧。希望可以帮助到大家。感兴趣的可以看下面几篇文章。
1. 程序调试 (一) —— App Crash的调试和解决示例(一)
2. 程序调试 (二) —— Xcode Simulator的高级功能(一)
3. 程序调试 (三) —— Xcode Simulator的高级功能(二)
4. 程序调试 (四) —— Xcode内存管理(一)

开始

首先看下主要内容:

使用 Xcode build settings.xcconfig文件以不同的构建配置(build configurations)更改应用程序的设置和图标。内容来自翻译

下面看下写作环境:

Swift 5, iOS 14, Xcode 12

下面就是正文啦。

Debug, test, release —— 这些是大多数应用程序经历的阶段。 在每个阶段,应用程序都有不同的构建设置、定义和常量。 开发人员使用debug后端 URL 和设置构建应用程序。 测试人员使用类似生产的设置测试 beta 版本。 客户使用具有最终生产设置的应用程序。

Xcode 中跨不同环境管理这些设置非常耗时——更不用说当您有多个targets时增加的工作了。 幸运的是,Apple 提供了一种更好的方式来处理这些设置:Xcode build configuration files, or .xcconfig files

在本教程中,您将:

  • 使用 Xcode 构建配置文件。
  • 管理跨多个环境和targets的构建设置。
  • 从代码访问构建设置。

打开起始项目。 构建并运行。

您将使用的应用程序 NinjaCounter 可帮助生物学家和爱好者计算海龟孵化的数量。 该应用程序有一个视图:CounterView.swift,用户可以在其中记录幼龟。

NinjaCounter 组中,您会发现以下内容:

  • Hatchling:一个简单的结构,具有孵化记录属性。
  • UserDefaultsHelper:一个 UserDefaults 助手,提供存储和加载孵化记录的方法。

构建并运行。 在tag text field中,输入 Leonardo。 点击+ Hatchling按钮。

这样,您就创建了一个带有孵化标签和孵化时间的新记录。

现在您已经了解了入门项目的要点,您将设置app widget


Setting Up Widget with App Group

打开 Widget.swift 并查看代码。 它创建了一个简单的widget,显示计数的孵化数量和报告的最后一个孵化的标签。

getTimeline(in:completion:) 中,widget使用 UserDefaultsHelpergetRecordsCount()getRecords()UserDefaults 获取数据。

选择 WidgetExtension scheme。 构建并运行。

目前,即使您刚刚录制了 Leonardo,该widget也不显示任何数据。 这是因为扩展程序无权访问应用程序的 UserDefaults。 要解决此问题,您需要将应用程序和小部件添加到app group

在项目导航器中选择 NinjaCounter 项目以显示Project Editor。 选择 NinjaCounter target。 打开Signing & Capabilities选项卡。

选择development team进行签名。

Bundle Identifier更改为您独有的内容,例如 com.myorg.NinjaCounter。 请记住这一点,因为您将再次需要它。

单击+ Capability。 双击App Groups

现在您已将App Groups添加到capabilities中,单击 + 按钮创建app group。 您将看到输入组名称的提示。

输入group,单击OK

现在,对 Widget Extension target执行相同的步骤。 确保bundle identifier.widget 结尾,但使用您为主应用程序创建的相同app group。 这允许在主机应用程序和widget extension之间共享数据。

现在您已经创建了App Group并将targets添加到其中,是时候让应用程序组访问 UserDefaults套件了。

打开 UserDefaultsHelper.swift 并将defaults声明替换为:

static private let defaults = UserDefaults(
  suiteName: "<#the app group name you defined#>")
  ?? .standard

使用此代码,您可以确保应用从app group共享的 UserDefaults套件中保存和读取数据。 这允许widget访问孵化数据。

active scheme更改为 NinjaCounter。 构建并运行。

您添加的记录不再存在,因为您使用的是不同的 UserDefaults 套件。 再次加入Leonardo

active scheme更改为WidgetExtention。 构建并运行。

您现在可以看到widget显示添加的记录。 恭喜!

在下一部分中,您将探索 Xcode 中的构建设置。


Demystifying Build Settings and Build Configurations in Xcode

在本节中,您将看到 Xcode 如何显示和解析构建设置。 打开项目编辑器。 找到TARGETS列表。 选择 NinjaCounter 作为app target

选择Build Settings选项卡。 选择 AllLevels 构建设置过滤器选项。

在这里,您可以看到app target的构建设置。 构建设置分为四列,显示不同范围内的设置值。

  • Resolved:解析优先级后的实际值。
  • NinjaCounter (target):显示在target级别设置的值。 target构建设置的优先级高于project的优先级。 默认情况下,targetproject构建设置继承值。
  • Ninja Counter (project):显示在project的构建设置中设置的值。 常规构建设置在项目project级别可用,其他仅适用于target
  • iOS Default:显示设置的 iOS 默认值。

注意:构建设置遵循以下优先级,从低到高:

Platform defaults
Project.xcconfig file
Project file build settings
Target .xcconfig file
Target build settings

选择 WidgetExtension target。 查看构建设置。

您可以在widget’s target level看到相同的设置。

Settings有多个值,每个Build Configuration一个。 例如,查看Base SDK。 构建配置就像一个环境。

您可以在项目级别全局定义Build ConfigurationsXcode为您创建两个配置:Debug and Release

对于这些环境,构建设置的默认值是不同的。 例如,Clang Optimization LevelDebug 中设置为 None -O0,让您调试和检查代码。 同时,在 Release 中,它默认为 Fastest, Smallest -Os 以实现最大的代码优化和最小的可执行文件大小。

现在您已经介绍了build settings,是时候回顾一下targets and schemes了。


Understanding Targets and Schemes

target指定单个产品及其构建设置和文件。 目标可以是应用程序、扩展程序、框架、iMessage 应用程序、应用程序剪辑等。

创建小widget时,Xcode 创建了一个新target。 您为应用程序和widget target配置了开发团队和功能。

Apple 将 Xcode scheme定义如下:“An Xcode scheme defines a collection of targets to build, a configuration to use when building, and a collection of tests to execute.”

创建一个新target会自动创建一个与之配套的新scheme

要查看此内容,请单击活动方案。 单击Edit Scheme…

专业提示:通过按住 Option 键单击active scheme,直接进入scheme editor,无需通过菜单。

您将看到scheme editor

例如,WidgetExtention scheme定义了 Xcode 将如何build, run, test, profile, analyze and archive widget target。 它还定义了要与这些操作一起使用的构建配置。

您可以看到 Run 默认为 Debug 配置,而 Archive 默认为 ReleaseBuild Configuration下拉菜单是您更改所选构建配置的地方。

关闭scheme editor。 接下来,您将为临时环境创建一个新的构建配置。


Creating a Staging Environment Configuration

要配置和创建应用程序的测试构建,您需要一个暂存构建配置。

打开Project Editor并选择 NinjaCounter PROJECT。 打开Info选项卡。

这是您定义构建配置的地方。 它们是全局的,并且在targets之间共享。 单击 + 添加一个新的。

选择Duplicate “Debug” Configuration。 将新配置命名为 Staging

您将看到新的Staging配置。 选择 NinjaCounter target并切换到 Build Settings 选项卡。

这些设置现在具有新构建配置的新值。 您从 Debug 复制了build configuration,因此它们具有相同的值。

现在您已经创建了临时环境,是时候添加 .xcconfig 文件了。


Creating Configuration Settings Files

使用 Build Settings选项卡修改设置有一些缺点。 搜索具有不同范围的长列表非常耗时,尤其是当您有多个targetsbuild configuration和设置需要管理时。 .xcconfig 文件是简化此过程的替代方法。

在项目导航器中选择 NinjaCounter 组。 创建一个新group。 命名组为Config Files。 这是您放置配置文件的地方。

单击File ▸ New ▸ File…

在文件模板列表中选择Configuration Settings File

将文件命名为 Debug.xcconfig。 对于此应用程序,您需要为每个配置创建一个 .xcconfig 文件。 创建 Staging.xcconfigRelease.xcconfig

注意:不要将 .xcconfig 文件添加到target memberships。 配置设置文件旨在成为开发依赖项(development dependencies)。 它们不应包含在app archive中的资源中。

接下来,您将在 Xcode 中设置配置文件。


Working With Configuration Settings Files

要使用配置文件,您需要在 Xcode 中设置它们。 在此之前,添加app display name的设置。

打开 Debug.xcconfig。 添加下面的设置并保存。

APP_NAME = Ninja Counter

.xcconfig 文件中的设置使用以下语法:SETTING_NAME = VALUE

注意:即使字符串值包含空格,也不要像 Ninja Counter 那样用引号将字符串值括起来。 例外情况是,包含空格的字符串在string list中时,字符串列表是以空格分隔的字符串值列表。

打开项目编辑器并选择 NinjaCounter 项目。 单击Info选项卡。

Configurations部分是您设置配置文件的地方。

如您所见,您可以在project level以及每个target level为每个环境使用配置文件。

Based on Configuration File下,单击配置文件选项。

Xcode 会显示您创建的 .xcconfig 文件列表。 将每个构建配置对应的配置文件设置为app and widget targets,如下图:

  • Debug
    • NinjaCounter: Debug
    • WidgetExtension: Debug
  • Staging
    • NinjaCounter: Staging
    • WidgetExtension: Staging
  • Release
    • NinjaCounter: Release
    • WidgetExtension: Release

此时,您已经为每个环境创建了一个配置文件。 做得好! 接下来,您将为每个环境自定义设置。

1. Creating a User-Defined Setting to Change the Bundle Display Name

作为开发人员,您可以在应用的所有阶段使用和使用您的应用。 当您在不同版本之间切换时,了解您当前安装的是哪个版本可能会令人困惑。 因此,使用不同的display names很有用。

打开 Debug.xcconfig。 将 APP_NAME的值更改为 Ninja CounterDev。 这是您在安装调试版本时将看到的应用程序显示名称。

现在,您一目了然就知道这是您正在使用绝密和下一代功能进行的开发版本!

接下来,您需要更改其他配置的名称。 打开 Staging.xcconfig 并添加以下行:

APP_NAME = Ninja CounterQA

最后,打开 Release.xcconfig 并使用生产display name更新设置:

APP_NAME = Ninja Counter

接下来,在Project navigator中选择项目本身。 选择 NinjaCounter 应用程序target

打开Build Settings选项卡。 滚动到列表末尾。

现在,您可以看到用户定义的构建设置 APP_NAME 和显示配置文件级别值的新设置范围。 如果您在这里看到您在配置文件中设置的值,那么您就走对了!

更改应用程序display name的最后一步是将 Bundle display name设置为引用您的用户定义设置。

打开 NinjaCounterInfo.plist。 添加Bundle display name属性。 将该属性的值设置为 $(APP_NAME)

或者,您也可以从Project EditorInfo选项卡中添加它。

确保将 NinjaCounter 设置为active scheme

构建并运行。 按 Shift-Command-H 进入主屏幕。

现在,您可以看到您的开发构建应用程序名称。

是时候在 Staging 环境中测试应用程序了。 按住 Option 键并单击 Xcode 任务栏中的运行按钮。

接下来,选择 Run 操作,然后选择 Info 选项卡,然后单击 Build Configuration 下拉菜单。 您的新staging configuration在列表中。 选择Staging作为构建配置。

单击Run,等待应用程序启动,然后返回 iOS 主屏幕。

应用程序显示名称现在是 NinjaCounterQA,可立即显示您正在使用的构建版本。

接下来,您将创建一个基本配置文件以避免冗余值。

2. Retaining Values With Inheritance

在您的配置文件中,您将不同构建的 APP_NAME 设置为 Ninja CounterDev、Ninja CounterQA 和 Ninja Counterrelease build 名称 NinjaCounter 是基本值,您可以在每个名称中重复该值。

project level设置常规构建设置是最佳实践。 然后,在不同的配置中重复使用这些设置以避免冗余。 在这种情况下,您将在每个构建配置中更改 APP_NAME 以从基本值继承。

右键单击Config files group。 选择New file…。 创建一个名为 Base.xcconfig 的新配置文件并添加以下行:

APP_NAME = Ninja Counter

接下来,替换其他配置文件的内容如下。

Debug.xcconfig:

#include "Base.xcconfig"

APP_NAME = $(inherited)Dev

Staging.xcconfig:

#include "Base.xcconfig"

APP_NAME = $(inherited)QA

Release.xcconfig:

#include "Base.xcconfig"

在这里,您更改了三个配置文件以从 Base.xcconfig 继承和重用基本设置。 您通过使用 $(inherited)并附加特定于每个构建的名称部分来完成此操作。

注意: $(inherited)值是从包含的文件中引用的(如果有),并遵循前面提到的优先级。 同样,如果您在配置文件中继承系统构建设置,则将根据优先级设置解析值。

最后,在 Staging 中构建并运行应用程序。

应用名称显示正确。 请注意,您不需要将 APP_NAME 定义添加到 Release.xcconfig。 那是因为设置会回退到继承的值。

要查看设置在 Xcode 中的显示方式,请打开Project Editor。 选择 NinjaCounter target,然后选择Build Settings。 滚动到User-defined部分。

尽管在应用程序运行时显示正确,但解析的值看起来并不正确。 这是因为 Base.xcconfig 未在Project EditorConfigurations部分中设置。 但是,继承在运行时按预期解析。

要解决此问题,请在项目编辑器中选择项目。 在Configurations下,在所有三个配置的项目级别设置 Base.xcconfig

现在,返回app targetBuild Settings

这些值现在在 Xcode 中正确显示。 请注意,新范围显示了项目级配置文件。

接下来,您将添加更多自定义设置。

3. Referencing Values of Other Settings

现在您的环境已准备就绪,是时候添加更多设置了。 将以下内容添加到 Base.xcconfig。 请务必替换您的bundle identifier

BASE_BUNDLE_IDENTIFIER = <your bundle identifier>

PRODUCT_BUNDLE_IDENTIFIER = $(BASE_BUNDLE_IDENTIFIER)

BASE_BUNDLE_IDENTIFIER 是包含项目基本bundle identifier的用户定义设置,而 PRODUCT_BUNDLE_IDENTIFIER 是指定bundle identifierXcode 构建设置。

在这里,您引用了 BASE_BUNDLE_IDENTIFIER 的值。 参考语法为:$(OTHER_BUILD_SETTING_NAME)

注意:当您从 Info.plist.entitlements 文件中引用构建设置时,您使用相同的引用语法。

现在,如果您要更改 Base.xcconfigBASE_BUNDLE_IDENTIFIER 的值,您将看不到构建设置中反映的更改。 那是因为bundle identifier目前在target’s settings中是硬编码的。 要解决此问题,请返回到Project Editor并选择NinjaCounter target。 在搜索字段中,输入 bundle 以缩小设置显示的数量。 双击target build setting并将其值更改为:

$(inherited)

之前,您将 UserDefaultsHelper.swift 中的代码更改为使用app group identifier作为 UserDefaults 套件名称,如下所示。

UserDefaults(suiteName: "group.<your bundle identifier>")

为了避免像套件名称这样的硬编码值,请在 Base.xcconfig 中的 PRODUCT_BUNDLE_IDENTIFIER 下添加以下设置:

USER_DEFAULTS_SUITE_NAME = group.$(BASE_BUNDLE_IDENTIFIER)

由于 NinjaCounter 中的app group identifier取决于bundle identifier,因此您创建了 USER_DEFAULTS_SUITE_NAME。 它内联引用 BASE_BUNDLE_IDENTIFIER 并附加group.前缀。 稍后,您将更新您的代码以使用此新设置,但首先您必须更新应用程序以使用该设置。 在项目导航器中,您将找到两个 .entitlements 文件。 打开 WidgetExtension.entitlements 并单击disclosure arrow以打开设置。 将 Item 0 的值更改为:

$(USER_DEFAULTS_SUITE_NAME)

接下来,打开 NinjaCounter.entitlements 并执行相同的操作。

您通过引用其他设置的值来确保依赖于其他设置的构建设置的一致性。 接下来,您将在代码中使用该设置并删除硬编码的设置。

4. Accessing Settings From Code

要从代码访问构建设置,您首先需要在 Info.plist 中添加一个引用属性。 打开 NinjaCounterInfo.plist并添加一个名为的自定义属性:

USER_DEFAULTS_SUITE_NAME

使用下面的值:

$(USER_DEFAULTS_SUITE_NAME)

widgetInfo.plist 执行相同操作,因为widget也需要访问设置。

接下来,在 NinjaCounter组中创建一个新的 Swift 文件并将其命名为 Config.swift。 打开文件并添加以下代码:

enum Config {
  static func stringValue(forKey key: String) -> String {
    guard let value = Bundle.main.object(forInfoDictionaryKey: key) as? String
    else {
      fatalError("Invalid value or undefined key")
    }
    return value
  }
}

stringValue(forKey:) 是一种辅助方法,可简化从 Info.plist 检索值的过程。 它调用 Bundleobject(forInfoDictionaryKey:)来获取字符串值。

注意:您可以从属性列表中存储和获取其他数据类型,例如布尔值和数组。 为简单起见,由于 NinjaCounter 只需要字符串值,因此此辅助方法仅适用于字符串。

然后,在文件检查器中,除了 NinjaCounter 之外,还选择 WidgetExtension 作为target

打开 UserDefaultsHelper.swift 并再次将defaults声明替换为:

static private let defaults = UserDefaults(
  suiteName: Config.stringValue(forKey: "USER_DEFAULTS_SUITE_NAME"))
  ?? .standard

在这里,您更改了 UserDefaultsHelper 的代码以从构建设置中获取套件名称。 您使用了 Config 的方法 stringValue(forKey:) 来获取值。

接下来,您将添加条件设置。

5. Adding Conditions

您可以添加条件来构建设置以针对特定的 SDK, architecturebuild configurationtarget。 在添加不特定于targetproject范围设置时,这尤其有用。

Base.xcconfig 中,添加以下行:

ONLY_ACTIVE_ARCH[config=Debug][sdk=*][arch=*] = YES

在这里,当应用程序在Debug configuration中构建时,您将 ONLY_ACTIVE_ARCH 设置为 YES。 因此,它通过仅构建active architecture来加快构建时间。

条件设置遵循以下语法:

BUILD_SETTING_NAME[条件=值] = VALUE

将条件值设置为星号意味着任何值。 您在上面添加的设置适用于任何 sdk 和任何architecture,但仅适用于Debug configuration

在下一部分中,您将自定义应用程序在不同环境中的行为。


Creating Record Stores for Each Environment

现在您的环境已准备就绪,您将孵化器的记录存储在不同的 UserDefaults 键中 —— 每个环境一个。 由于应用在本地存储数据,这里的 UserDefaults 是应用的“back end”

是时候在环境的配置文件中添加 UserDefaults 键了。

Debug.xcconfig 中,添加:

USER_DEFAULTS_RECORDS_KEY = HatchlingsRecords-Debug

Staging.xcconfig 中,添加:

USER_DEFAULTS_RECORDS_KEY = HatchlingsRecords-Staging

Release.xcconfig 中,添加:

USER_DEFAULTS_RECORDS_KEY = HatchlingsRecords

密钥名称在每个环境中都会发生变化。

接下来,将以下属性添加到 NinjaCounter 和扩展的 Info.plist 中。

Key

USER_DEFAULTS_RECORDS_KEY

Value

$(USER_DEFAULTS_RECORDS_KEY)

之后,打开 UserDefaultsHelper.swift 并将recordsKey的声明替换为:

static private let recordsKey = Config
  .stringValue(forKey: "USER_DEFAULTS_RECORDS_KEY")

正如您从构建设置中获得 UserDefaults 套件名称一样,在上面的更改中,您使用 stringValue(forKey:)将硬编码的 UserDefaults 键替换为相应的构建设置值。

最后,您已准备好测试您的更改! 将active schemeBuild Configuration更改为Debug

构建并运行。 添加Donatello记录。

接下来,将Build Configuration更改为Release。 构建并运行。

您看不到 Donatello 的记录,因为您将其存储在 Debug 存储区中。 将Build Configuration更改回Debug。 构建并运行。

您在使用debug build configuration运行时存储的记录就在那里。 现在,您知道您的环境按预期工作。

这是否让您想起了不同的方法? 看看下面的代码。

#if DEBUG
let recordsKey = "HatchlingsRecords-Debug"
#elseif STAGING
let recordsKey = "HatchlingsRecords-Staging"
#else
let recordsKey = "HatchlingsRecords"
#endif

条件编译是一种流行的做法。 但是,使用它来管理跨环境的常量有一个缺点,即您将可配置的常量与代码混合在一起。 使用build settings来存储这些值并在代码中使用它们是一个不错的选择。

那很简单! 在配置文件中设置设置不仅可以简化构建设置的管理,还可以让您从 Xcode 外部化设置。 这使它们具有可移植性,并使跟踪源代码管理中的更改更加方便。

在下一部分中,您将为每个target创建配置文件。


Using Configuration Files for Each Target

该应用程序现在在所有三种环境中都可以正常运行。 但是,如果您需要为 WidgetExtention 创建或外部化target-specific的构建设置怎么办? 接下来,您将看到如何为widget target创建配置文件。

Config files group中,创建以下配置文件:

  • WidgetBase.xcconfig:
#include "Base.xcconfig"

PRODUCT_BUNDLE_IDENTIFIER = $(BASE_BUNDLE_IDENTIFIER).widget
  • WidgetDebug.xcconfig
#include "Debug.xcconfig"
#include "WidgetBase.xcconfig"
  • WidgetStaging.xcconfig
#include "Staging.xcconfig"
#include "WidgetBase.xcconfig"
  • WidgetRelease.xcconfig
#include "Release.xcconfig"
#include "WidgetBase.xcconfig"

在这里,您通过将 BASE_BUNDLE_IDENTIFIER 附加到您的基本设置来设置widget PRODUCT_BUNDLE_IDENTIFIER

接下来,在Project Editor中为 WidgetExtension target将文件设置为各自的环境,如下所示。

  • Debug
    • Project: Base
    • NinjaCounter: Debug
    • WidgetExtension: WidgetDebug
  • Staging
    • Project: Base
    • NinjaCounter: Staging
    • WidgetExtension: WidgetStaging
  • Release
    • Project: Base
    • NinjaCounter: Release
    • WidgetExtension: WidgetRelease

注意:您不需要设置 WidgetBase.xcconfig,因为其他文件都包含它。

最后,返回到 Project Editor 并将 WidgetExtension targetProduct Bundle Extension 设置更改为:

$(inherited)

之后,将active scheme更改为 WidgetExtension。 确保构建配置为Debug。 构建并运行。

widget现在显示您为 Donatello 添加的最新记录。 尽管 WidgetDebug.xcconfig 不包含任何设置,但它包含来自 WidgetBase.xcconfigDebug.xcconfig 的设置。 这就是当您为widget设置 Debug.xcconfig 时它起作用的原因。


Differentiating the App Icon for Non-Release Builds

一些利益相关者,如测试人员和 QA 工程师,使用多个版本的应用程序。 例如,他们可能会使用 App Store 版本和开发人员或 TestFlight提供的 Staging 版本。 为了让您同事的生活更轻松,您的下一步将是更改应用程序图标,以区分 DebugStaging 版本与 Release 版本。

在项目中,您将在应用程序的 Assets.xcassets 中找到另一个名为 AppIcon-NonProd 的应用程序图标集。

设置资产目录应用图标集的名称,需要修改配置文件中的ASSETCATALOG_COMPILER_APPICON_NAME

Debug.xcconfigStaging.xcconfig 中,添加:

ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-NonProd

Release.xcconfig中,添加:

ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon

注意:正如您在 Project EditorBuild Settings 选项卡和 Info.plist 中看到的,有许多构建设置。 你不必记住每个的名字。 相反,请使用 Apple 文档中的build settings reference

active scheme设置为 NinjaCounter。 将Build Configuration设置为Debug。 构建并运行。 进入主屏幕。

图标没有改变,因为Target Build Settings具有更高的优先级。 要解决此问题,请从项目编辑器转到 NinjaCounter targetBuild Settings ,然后找到Asset Catalog App Icon Set Name

在配置文件的范围内,您可以看到添加到配置文件中的值。 但是,该设置解析为target scope内的值。

要解决此问题,请将app target范围的父值更改为 $(inherited)

解析的值现在与配置文件匹配。

构建并运行。 进入主屏幕。

该应用程序现在显示 AppIcon-NonProd 应用程序图标集中的图标。

注意:在widget的资产目录中,有一个名为 AppIcon-NonProd 的空应用图标集。虽然它没有被使用,但它解决了编译时错误,Xcode 会警告widget的资产目录中不存在 AppIcon-NonProd。这是因为widget targetapp target继承 ASSETCATALOG_COMPILER_APPICON_NAMEXcode将在编译时检查该值。

在本教程中,您使用 .xcconfig 文件来外部化构建设置。你涵盖:

  • 跨不同build configurations管理设置。
  • 针对不同环境和targets使用配置文件的多种方法。
  • 从代码访问构建设置。

有关build configuration and app distribution best practices的更多信息,请查看我们iOS App Distribution & Best Practices一书。

以下是 Apple 文档中的一些更有用的资源:

后记

本篇主要讲述了使用Build Configurations.xcconfig 构建你的App,感兴趣的给个赞或者关注~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 197,814评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,124评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 144,814评论 0 327
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,924评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,815评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,562评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,944评论 3 388
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,582评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,859评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,881评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,700评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,493评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,943评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,115评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,413评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,978评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,182评论 2 339

推荐阅读更多精彩内容