.xcconfig is an Xcode configuration file in “key=value” format that centrally manages Build Settings of a project. Instead of manually changing parameters in the Xcode UI for each configuration, developers describe them in a text file that can be versioned and reused across projects. According to Apple Developer Documentation, 2025, using .xcconfig reduces project setup time by 70% and eliminates configuration discrepancies between developers. .xcconfig files can inherit from each other, forming a chain of configurations.
Key Takeaways
.xcconfig (Xcode Configuration File) is a plain-text file that contains Build Settings in PARAMETER_NAME = value format. .xcconfig files are used for centralized management of Xcode build configurations: they replace manual editing of fields in the Build Settings UI. Each .xcconfig is linked to a Build Configuration (Debug, Release) or to the project as a whole and can override any build setting: SWIFT_VERSION, IPHONEOS_DEPLOYMENT_TARGET, PRODUCT_BUNDLE_IDENTIFIER, CODE_SIGN_STYLE, PROVISIONING_PROFILE_SPECIFIER.
Before .xcconfig appeared, build settings were stored only in project.pbxproj — a binary/plist file that is difficult to read in diffs and impossible to comment. .xcconfig solved this problem: developers can comment on parameters, group them by meaning, create versionable files for different environments, and inherit parameters between files. This made .xcconfig the de facto standard for configuration management in iOS projects.
.xcconfig files are located inside the project, usually in the Configurations/ or BuildConfig/ folder. Each file corresponds to one Build Configuration: Debug.xcconfig, Release.xcconfig, Staging.xcconfig. Additionally, a common Shared.xcconfig file is created, which is included in all configurations via #include. This allows defining common parameters once and overriding specific ones in configuration files.
Diff readability: changes in .xcconfig are visible in Git diff as regular lines. Unlike project.pbxproj, where changing field order results in 50 lines of changes for a single parameter edit. Comments: in .xcconfig you can explain why each parameter is needed. Inheritance: you can create a base configuration with common settings and override only the necessary parameters for Debug and Release.
The .xcconfig syntax is as simple as possible: each line is a parameter, name and value separated by an equals sign. Spaces around = are ignored. Values can contain variables in $(VARIABLE_NAME) or ${VARIABLE_NAME} format. Comments start with // or # and apply until the end of the line. Lines continue on the next line using a backslash \. Empty lines are ignored.
Variables in .xcconfig can reference other variables, creating composite values. For example: PRODUCT_NAME = MyApp, PRODUCT_BUNDLE_IDENTIFIER = com.example.$(PRODUCT_NAME). Xcode evaluates the value at build time, substituting the actual variable values. AGP also supports system variables: ARCHS, SDK_NAME, CONFIGURATION, PLATFORM_NAME, which are set by the build environment.
For conditional configuration, platform directives in square brackets are used: PARAMETER[sdk=iphoneos*] = value. For example, SUPPORTED_PLATFORMS[sdk=iphoneos*] = iphoneos sets the parameter only for iOS builds. Wildcards are supported: * (any characters), ? (single character). Conditional directives allow having one .xcconfig for multiple platforms and setting different values for iOS and macOS in a single file.
// Shared.xcconfig — common project settings
SWIFT_VERSION = 5.0
IPHONEOS_DEPLOYMENT_TARGET = 16.0
SDKROOT = iphoneos
TARGETED_DEVICE_FAMILY = 1,2
// Bundle identifier — assembled from prefix and name
BUNDLE_ID_PREFIX = com.example
PRODUCT_NAME = MyApp
PRODUCT_BUNDLE_IDENTIFIER = $(BUNDLE_ID_PREFIX).$(PRODUCT_NAME)
// Conditional setting for macOS
SUPPORTED_PLATFORMS[sdk=macosx*] = macosx
PRODUCT_BUNDLE_IDENTIFIER[sdk=macosx*] = $(BUNDLE_ID_PREFIX).$(PRODUCT_NAME).mac
// Versioning
MARKETING_VERSION = 2.4.1
CURRENT_PROJECT_VERSION = 37
# include is an .xcconfig preprocessor directive that includes the contents of another .xcconfig file. Directives can be nested: Shared.xcconfig can #include “Base.xcconfig”, Debug.xcconfig can #include “Shared.xcconfig”. The inheritance chain allows building a hierarchy of configurations, where each level overrides the parameters of the previous one. #include works on a last-write basis: if the same parameter is defined in both the included and the main file, the value from the main file takes priority.
The correct hierarchy for a typical iOS project: Base.xcconfig (most common parameters) → Shared.xcconfig (project settings) → Debug.xcconfig or Release.xcconfig. Base.xcconfig defines standards (SWIFT_VERSION, DEPLOYMENT_TARGET), Shared.xcconfig — project specifics (PRODUCT_NAME, PREPROCESSOR_DEFINITIONS), Debug/Release — environment (DEBUG_INFORMATION_FORMAT, OPTIMIZATION_CFLAGS). #include does not allow cycles — Xcode will throw an error if a circular dependency is detected.
Example: Config/Base.xcconfig → Config/iOS/Shared.xcconfig → Config/iOS/Debug.xcconfig. This structure allows reusing Base for iOS, macOS and tvOS projects, and Shared only for iOS. Note: #include uses a filename or relative path from the root .xcconfig location. Absolute paths are not recommended — they break builds on other machines and in CI/CD.
// --- Config/Base.xcconfig ---
SWIFT_VERSION = 5.0
ENABLE_MODULE_VERIFIER = YES
CLANG_ENABLE_MODULES = YES
// --- Config/iOS/Shared.xcconfig ---
#include "../Base.xcconfig"
IPHONEOS_DEPLOYMENT_TARGET = 16.0
PRODUCT_BUNDLE_IDENTIFIER = com.example.myapp
// --- Config/iOS/Debug.xcconfig ---
#include "Shared.xcconfig"
OPTIMIZATION_CFLAGS = -O0
DEBUG_INFORMATION_FORMAT = dwarf
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG
ENABLE_TESTABILITY = YES
// --- Config/iOS/Release.xcconfig ---
#include "Shared.xcconfig"
OPTIMIZATION_CFLAGS = -Osize
DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
SWIFT_COMPILATION_MODE = wholemodule
Connecting .xcconfig to a project is done in Project Info → Configurations. For each Build Configuration (Debug, Release, AdHoc), the corresponding .xcconfig is selected from the “Based on Configuration File” dropdown. If a configuration is not linked to a file, Xcode uses values from project.pbxproj. After selecting .xcconfig, all parameters from the file become active for that configuration.
It is important to distinguish between Project-level and Target-level configurations. A Project-level .xcconfig sets default parameters for all targets. A Target-level .xcconfig overrides them for a specific target. If a parameter is not set in the target-level .xcconfig, the value from the project-level is used. If it is not set there either, the value from project.pbxproj is used. Practical rule: place common parameters (build, versions) in project-level, and target specifics (bundle identifier, provisioning) in target-level.
In case of conflict between .xcconfig and UI Build Settings, the value from UI takes priority (it overrides .xcconfig). This can lead to confusion: a developer changes a Build Setting in UI, not realizing that .xcconfig specifies a different value. It is recommended to fully switch to .xcconfig and not touch UI Build Settings. To check which parameter is applied, use xcrun xcodebuild -showBuildSettings — the command will show the final values of all parameters after resolving all levels.
Consider a three-level configuration: Dev (local development), Staging (test server), Production (release). A separate .xcconfig is created for each environment, defining different API_URL, logging, and certificate values. Dev uses localhost, Staging uses staging.api.example.com, Production uses api.example.com. All three inherit the common Shared.xcconfig via #include.
The key parameter that differs between environments is PRODUCT_BUNDLE_IDENTIFIER. For Dev: com.example.myapp.dev, for Staging: com.example.myapp.staging, for Production: com.example.myapp. Different bundle IDs allow installing all three versions on a single device simultaneously. Also different are CODE_SIGN_IDENTITY (Apple Development for Dev, Apple Distribution for Production) and PROVISIONING_PROFILE_SPECIFIER.
To pass values into code, INFOPLIST_PREFIX_HEADER or OTHER_SWIFT_FLAGS with -D preprocessor is used. Swift does not have a preprocessor, so Active Compilation Conditions are used: SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEV. In code: #if DEV; #elseif STAGING; #else; #endif. For Objective-C, GCC_PREPROCESSOR_DEFINITIONS is used. This allows compiling different code for different environments without changing the source files.
// --- Config/Dev.xcconfig ---
#include "Shared.xcconfig"
PRODUCT_BUNDLE_IDENTIFIER = $(BUNDLE_ID_PREFIX).$(PRODUCT_NAME).dev
CODE_SIGN_IDENTITY = Apple Development
PROVISIONING_PROFILE_SPECIFIER = Dev Profile
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG DEV
OTHER_SWIFT_FLAGS = -D DEV
// API URL via Info.plist — value is substituted
API_BASE_URL = http://localhost:3000/api
// --- Config/Staging.xcconfig ---
#include "Shared.xcconfig"
PRODUCT_BUNDLE_IDENTIFIER = $(BUNDLE_ID_PREFIX).$(PRODUCT_NAME).staging
CODE_SIGN_IDENTITY = Apple Development
PROVISIONING_PROFILE_SPECIFIER = Staging Profile
SWIFT_ACTIVE_COMPILATION_CONDITIONS = STAGING
OTHER_SWIFT_FLAGS = -D STAGING
API_BASE_URL = https://staging.api.example.com/v2
// --- Config/Production.xcconfig ---
#include "Shared.xcconfig"
PRODUCT_BUNDLE_IDENTIFIER = $(BUNDLE_ID_PREFIX).$(PRODUCT_NAME)
CODE_SIGN_IDENTITY = Apple Distribution
PROVISIONING_PROFILE_SPECIFIER = AppStore Distribution
SWIFT_ACTIVE_COMPILATION_CONDITIONS = RELEASE
API_BASE_URL = https://api.example.com/v3
Values from .xcconfig can be passed into Info.plist through variables $(PARAMETER_NAME). If a parameter is defined in .xcconfig (e.g., API_BASE_URL), it can be used in Info.plist: <key>ApiBaseUrl</key><string>$(API_BASE_URL)</string>. At build time, Xcode replaces $(API_BASE_URL) with the value from .xcconfig. This allows configuring the application without changing code — just switch the scheme.
.xcconfig parameters used in Info.plist must be public — they end up in the binary and are visible in the decompiled application. Do not use .xcconfig for secret values (tokens, passwords) — use services like Firebase Remote Config, running on the server. .xcconfig for Info.plist is suitable for: server URLs, entity names, tracker identifiers, feature flags.
Access to Info.plist values in code: Bundle.main.object(forInfoDictionaryKey: “ApiBaseUrl”) for Objective-C/Swift. If the value is set via .xcconfig, it will be substituted and available in Bundle main.infoDictionary. This method is preferable to BuildConfigField (as in Android) because Info.plist is a standard iOS mechanism, and its values are available to all system components, including extensions, widgets, and Siri Intents.
Frequently Asked Questions
User-Defined Setting is a custom parameter added through the UI Build Settings. It works the same as .xcconfig, but it cannot be versioned, commented, or reused across projects. .xcconfig is a file on disk, User-Defined Setting is an entry in project.pbxproj.
Yes, CocoaPods generates Pods-*.xcconfig files for each configuration. These files contain settings for connecting pods. Pods.xcconfig is automatically linked to your .xcconfig via #include in the generator file. Do not edit Pods.xcconfig manually — it is overwritten during pod install.
Through Info.plist: define a parameter in .xcconfig and use $(PARAM) in Info.plist. In code: Bundle.main.infoDictionary[“PARAM”]. For preprocessor flags, use SWIFT_ACTIVE_COMPILATION_CONDITIONS and #if CONDITION.
Reasons: you changed the value in UI Build Settings (UI overrides .xcconfig); the file is not linked to the configuration (check Project → Info → Configurations); incorrect #include path; typo in parameter name. Diagnosis: xcodebuild -showBuildSettings will show all active parameters.
Yes, .xcconfig does not depend on the UI framework. For SwiftUI projects, .xcconfig is equally useful: managing bundle ID, versions, environment configurations, SWIFT_ACTIVE_COMPILATION_CONDITIONS for feature flags. SwiftUI does not provide an alternative to .xcconfig, so it is recommended to use it with any project.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also