软件开发最佳实践开处方严格分离配置和代码。然而,苹果平台上的开发人员往往很难将这些指导原则与xcode的项目密集型工作流程相结合。
了解每个项目设置所做的事情,以及它们是如何相互作用的,这是一项需要多年磨练的技能。事实上,大部分这些信息都隐藏在Xcode的GUI中,这对我们没有好处。
导航到项目编辑器的“BuildSettings”选项卡,您将受到千百的构建设置分布在项目、目标和配置的各个层-更不用说其他六个标签了!
幸运的是,有一种更好的方法来管理所有这些配置,而不需要通过错综复杂的选项卡和泄露箭头进行单击。
本周,我们将向您展示如何使用基于文本的xcconfig将Xcode的构建设置外部化的文件,使您的项目更紧凑、更容易理解和更强大。
检查XcodeBuildSettings.com对于最新版本的Xcode所支持的每个构建设置的完整引用。
Xcode构建配置文件,更常见的是xcconfig文件扩展名,允许在没有Xcode的情况下声明和管理应用程序的构建设置。它们是纯文本,这意味着它们对源代码控制系统更友好,可以用任何编辑器进行修改。
从根本上说,每个配置文件都由一系列键值赋值组成,其语法如下:
例如,要为项目指定SWIFT语言版本,可以指定SWIFT_VERSION构建设置如下:
根据posix标准环境变量的名称仅由大写字母、数字和下划线组成(_)-我喜欢叫这个会议SCREAMING_SNAKE_CASE🐍🗯.
一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS交流群:1012951431, 分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!希望帮助开发者少走弯路。
乍一看,xcconfig档案与.env文件,带有简单的、换行符分隔的语法。但是,Xcode构建配置文件的功能远不止这些。看啊!
保留现有价值
若要追加而不是替换现有定义,请使用$(inherited)变量如下:
BUILD_SETTING_NAME = $(inherited)additional value
通常这样做是为了建立值列表,例如编译器在其中搜索框架以查找包含的头文件的路径(FRAMEWORK_SEARCH_PATHS):
FRAMEWORK_SEARCH_PATHS = $(inherited) $(PROJECT_DIR)
Xcode按以下顺序分配继承的值(从最低到最高优先级):
- 平台缺省值
- Xcode项目xcconfig文件
- Xcode项目文件生成设置
- 目标xcconfig文件
- 目标生成设置
空格用于分隔字符串和路径列表中的项。若要指定包含空格的项,必须用引号(").
引用值
可以用以下语法将其他设置的值替换为它们的声明名:
BUILD_SETTING_NAME = $(ANOTHER_BUILD_SETTING_NAME)
替换可以根据现有值定义新变量,也可以使用内联方式动态地构建新值。
OBJROOT = $(SYMROOT)
CONFIGURATION_BUILD_DIR = $(BUILD_DIR)/$(CONFIGURATION)-$(PLATFORM_NAME)
为引用的生成设置设置回退值
在Xcode 11.4及更高版本中,可以使用default如果引用的生成设置计算结果为空,则计算运算符将指定要使用的回退值。
$(BUILD_SETTING_NAME:default=value)
限定生成设置
您可以根据它们的SDK(sdk),建筑(arch),和/或配置(config)根据以下语法:
BUILD_SETTING_NAME[sdk=sdk] = value for specified sdk
BUILD_SETTING_NAME[arch=architecture] = value for specified architecture
BUILD_SETTING_NAME[config=configuration] = value for specified configuration
给定相同构建设置的多个定义之间的选择,编译器将根据特殊性进行解析。
BUILD_SETTING_NAME[sdk=sdk][arch=architecture] = value for specified sdk and architectures
BUILD_SETTING_NAME[sdk=*][arch=architecture] = value for all other sdks with specified architecture
例如,可以指定以下生成设置,仅通过为活动体系结构进行编译来加快本地生成:
ONLY_ACTIVE_ARCH[config=Debug][sdk=*][arch=*] = YES
包括来自其他配置文件的生成设置
生成配置文件可以包含来自其他配置文件的设置。#include语法作为等效C此功能所基于的指令:
#include "path/to/File.xcconfig"
正如我们在本文后面将看到的那样,您可以利用这一点以非常强大的方式构建构建设置的级联列表。
通常,当编译器遇到#include指令无法被解析,则会引发错误。但xcconfig文件还支持#include?指令,如果找不到文件,则不会发出任何抱怨。
在很多情况下,您不希望文件的存在或不存在来更改编译时行为;毕竟,构建是最好的,因为它们是可预测的。但是,您可以将它用作可选开发工具的挂钩,例如揭示,它需要以下配置:
# Reveal.xcconfig
OTHER_LDFLAGS = $(inherited) -weak_framework RevealServer
FRAMEWORK_SEARCH_PATHS = $(inherited) /Applications/Reveal.app/Contents/SharedSupport/iOS-Libraries
创建生成配置文件
若要创建构建配置文件,请选择“File>NewFile…”菜单项(⌘n),向下滚动到标记为“Other”的部分,并选择ConfigurationSettings文件模板。接下来,将其保存在项目目录中的某个位置,确保将其添加到所需的目标中。
一旦您创建了一个xcconfig文件,您可以为其关联目标将其分配给一个或多个生成配置。
构建配置文件不应包含在项目的任何目标中。如果你找到任何.xcconfig显示在应用程序中的文件.ipa归档,确保它们不是任何目标的成员,并且不会出现在任何“复制捆绑资源”构建阶段。
现在我们已经介绍了使用Xcode构建配置文件的基础知识,下面让我们看看如何使用Xcode构建配置文件来管理开发、阶段和生产环境的几个示例。
为内部构建定制应用程序名称和图标
开发iOS应用程序通常需要在模拟器和测试设备上处理各种内部构建(以及AppStore的最新版本,以供参考)。
你可以让事情变得更简单xcconfig为每个配置分配一个不同的名称和应用程序图标的文件。
// Development.xcconfig
PRODUCT_NAME = $(inherited) α
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-Alpha
//////////////////////////////////////////////////
// Staging.xcconfig
PRODUCT_NAME = $(inherited) β
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-Beta
跨不同环境管理常量
如果您的后端开发人员按照前面提到的12要素应用程序哲学上,它们将为开发、阶段和生产环境提供独立的端点。
在IOS上,管理这些环境的最常见方法可能是将条件编译语句与生成设置一起使用,如DEBUG.
import Foundation
#if DEBUG
let apiBaseURL = URL(string: "https://api.staging.example.com")!
#else
let apiBaseURL = URL(string: "https://api.example.com")!
#endif
这完成了工作,但与代码/配置分离的规范相冲突。
另一种方法采用这些特定于环境的值,并将它们放在它们所属的位置。xcconfig档案。
// Development.xcconfig
API_BASE_URL = api.staging.example.com
//////////////////////////////////////////
// Production.xcconfig
API_BASE_URL = api.example.com
xcconfig文件处理顺序//作为注释分隔符,不管它是否用引号括起来。如果你试图用反斜杠逃跑//,这些反斜杠将逐字逐句显示,必须从结果值中删除。这在指定每个环境的URL常量时特别不方便。
如果你不想改变这种不幸的行为,你总是可以略过这个计划,并准备好https://用密码。(您正在使用https…对吧?)
但是,要以编程方式提取这些值,我们需要执行另一个步骤:
从SWIFT访问生成设置
生成由Xcode项目文件定义的设置,xcconfig文件和环境变量仅在构建时可用。当您运行编译后的应用程序时,周围的上下文都是不可用的。(谢天谢地!)
但是等一下--你难道不记得以前在其他选项卡中看到过一些构建设置吗?信息,是吗?
正因为如此,“信息”选项卡实际上只是目标的一种花哨的表示形式。Info.plist档案。在构建的时候,Info.plist文件根据提供的构建设置进行编译,并复制到生成的应用程序中。束。因此,通过添加对$(API_BASE_URL),您可以通过infoDictionary基金会性质BundleAPI整洁!
按照这种方法,我们可以做如下事情:
import Foundation
enum Configuration {
enum Error: Swift.Error {
case missingKey, invalidValue
}
static func value<T>(for key: String) throws -> T where T: LosslessStringConvertible {
guard let object = Bundle.main.object(forInfoDictionaryKey:key) else {
throw Error.missingKey
}
switch object {
case let value as T:
return value
case let string as String:
guard let value = T(string) else { fallthrough }
return value
default:
throw Error.invalidValue
}
}
}
enum API {
static var baseURL: URL {
return try! URL(string: "https://" + Configuration.value(for: "API_BASE_URL"))!
}
}
当从呼叫站点查看时,我们发现这种方法与我们的最佳实践协调得很好--看不到一个硬编码常量!
let url = URL(string: path, relativeTo: API.baseURL)!
var request = URLRequest(url: url)
request.httpMethod = method
另外,如果你想一起进阶,不妨添加一下交流群1012951431,选择加入一起交流,一起学习。期待你的加入!
网友评论