Xcode Projects, Targets, Schemes and Previews
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production; see the section bibliography for the reference material consulted while preparing these pages. |
Getting Started with Apple Platforms created FieldNotes from Xcode’s multiplatform template and ran it. This page opens up what the template produced: the project file, its targets, the schemes that decide what Run and Test do, the build configurations and settings behind every build, and the canvas where SwiftUI previews and playgrounds run without launching the app. Everything here applies to Xcode 27 building Swift 6.4 code for iOS, iPadOS, macOS, watchOS and visionOS 27, with FieldNotes's deployment minimum kept at 26. Xcode 27-only features (Device Hub, argument grids in previews, the JSON project format) are called out as they come up.
The Swift toolchain behind Xcode (the compiler, swift build, SwiftPM) is covered in
Swift Build and Tooling; this page is about the Xcode
side only.
Workspace, Project, Target and Product
Four nouns describe every Xcode build:
-
A project (
FieldNotes.xcodeproj) is a bundle directory holding the list of source files, the targets, the build configurations and the project-level build settings. It is the unit you open, and it is what goes into source control. -
A target is one recipe for one product. FieldNotes has
FieldNotes(the multiplatform app),FieldNotesWatch(the watch app), and laterFieldNotesWidgets(a widget extension) andFieldNotesTests(a test bundle). Each target has its own build phases and its own build settings, which override the project’s. -
A product is what a target produces: an
.appbundle, an.appexextension, a framework, a test bundle. Products of one target can be embedded in another; the widget extension ends up insideFieldNotes.app/PlugIns/. -
A workspace (
.xcworkspace) groups several projects so their targets can depend on each other. Every project already contains an implicit workspace; you only create an explicit one when you combine projects. FieldNotes does not need one, because its shared code lives in local Swift packages (see Swift Packages and Modularizing an App), which a single project can reference directly.
On disk, FieldNotes looks like this:
FieldNotes/
├── FieldNotes.xcodeproj/ # the project bundle
│ ├── project.pbxproj # targets, files, settings (or project.xcproj, see below)
│ └── xcshareddata/xcschemes/ # shared schemes, committed
│ ├── FieldNotes.xcscheme
│ └── FieldNotesWatch.xcscheme
├── Config/ # .xcconfig files (see below)
├── FieldNotes/ # app sources, Assets.xcassets, FieldNotes.entitlements
├── FieldNotesWatch/ # watch app sources
└── Packages/
├── FieldNotesKit/ # FieldNotesModel, FieldNotesServices
└── FieldNotesUI/
The JSON Project Format (Xcode 27.2)
For twenty years the project’s configuration file has been project.pbxproj, an old-style property list
full of generated identifiers that is notoriously hard to review and merge. Xcode 27 adds a second format: a
smaller, hierarchical, self-describing JSON file named project.xcproj, still inside the .xcodeproj
bundle. From Xcode 27.2 new projects use it by default, and Xcode 27 opens both. Changes in the JSON file
map to what you did in Xcode, so diffs are readable, merge conflicts are rarer, and coding agents can edit it
safely. To convert an existing project, select it in the Project navigator and choose JSON in the File
inspector’s Project Format pop-up; the change is just the removal of one file and the addition of the other,
so it can be reverted from source control. A converted project can no longer be opened by Xcode 26, which
matters if part of a team, or a CI machine, lags behind.
# After switching FieldNotes to the JSON format, git shows one file swapped for another.
git status --short FieldNotes.xcodeproj
# D FieldNotes.xcodeproj/project.pbxproj
# ?? FieldNotes.xcodeproj/project.xcproj
Build Phases
A target’s Build Phases tab lists, in order, what happens to its inputs: target dependencies first (so the watch app and packages are built before they are embedded), then Compile Sources, Link Binary With Libraries, Copy Bundle Resources, Embed phases for extensions and frameworks, and any Run Script phases you add. Xcode runs independent tasks in parallel and signs the product last. A typical custom phase in FieldNotes is a script that fails the build when the watch and phone apps disagree on a version number:
# Run Script phase "Check version parity" in the FieldNotes target.
# Input file: $(SRCROOT)/Config/Base.xcconfig (declared so the phase can run in the script sandbox)
set -euo pipefail
if [ "${MARKETING_VERSION}" != "$(awk -F' = ' '/^MARKETING_VERSION/ {print $2}' "${SCRIPT_INPUT_FILE_0}")" ]; then
echo "error: MARKETING_VERSION does not match Config/Base.xcconfig"
exit 1
fi
Figure: Project Anatomy
The left half of the diagram is what gets built (project, targets, phases, products); the right half is how it gets built for a given purpose (scheme, actions, configurations). Keeping those two apart is what makes the rest of this page easy to follow.
Schemes and Their Actions
A scheme is the thing chosen in the toolbar next to the Run button. It names the targets to build and defines five actions, each with its own build configuration and options:
| Action | Default configuration | What it does for FieldNotes |
|---|---|---|
Run (Command-R) |
Debug |
Builds, installs on the run destination, launches and attaches the debugger. Options: launch arguments, environment variables, simulated location, app language and region. |
Test (Command-U) |
Debug |
Builds the test bundles and runs them, optionally through a test plan and with code coverage. See Unit Testing with Swift Testing. |
Profile (Command-I) |
Release |
Builds an optimized app and opens it in Instruments. See Performance and Instruments. |
Analyze (Shift-Command-B) |
Debug |
Runs the Clang static analyzer over C and Objective-C code, which matters only for mixed-language targets (see Mixing Swift, Objective-C and C++ in an App). |
Archive |
Release |
Builds a distribution-ready |
Xcode creates one scheme per app target and stores it in xcshareddata so the whole team shares it. The
scheme file is plain XML; this is the part of FieldNotes.xcscheme that matters, including an environment
variable that makes the Run action start with sample data:
<Scheme LastUpgradeVersion = "2700" version = "1.7">
<BuildAction parallelizeBuildables = "YES" buildImplicitDependencies = "YES">
<!-- BuildActionEntries: FieldNotes, and FieldNotesWatch as a dependency -->
</BuildAction>
<TestAction buildConfiguration = "Debug" codeCoverageEnabled = "YES">
</TestAction>
<LaunchAction buildConfiguration = "Debug" launchStyle = "0" debugDocumentVersioning = "YES">
<EnvironmentVariables>
<EnvironmentVariable key = "FIELDNOTES_SAMPLE_DATA" value = "1" isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction>
<ProfileAction buildConfiguration = "Release">
</ProfileAction>
<AnalyzeAction buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction buildConfiguration = "Release" revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
The app reads that variable like any other process environment, which keeps the switch out of the Release build entirely because only the Run action sets it:
import Foundation
extension NoteStore {
static func makeForLaunch() -> NoteStore {
let store = NoteStore()
if ProcessInfo.processInfo.environment["FIELDNOTES_SAMPLE_DATA"] == "1" {
store.notes = Note.samples
}
return store
}
}
Every scheme action also exists on the command line, which is how CI drives the same scheme (see CI/CD with Xcode Cloud and the Command Line):
xcodebuild -project FieldNotes.xcodeproj -list # targets, configurations, schemes
xcodebuild -scheme FieldNotes -destination 'platform=iOS Simulator,name=iPhone 17' test
xcodebuild -scheme FieldNotes -destination 'generic/platform=iOS' analyze
xcodebuild -scheme FieldNotes -destination 'generic/platform=iOS' \
-archivePath build/FieldNotes.xcarchive archive
Debug and Release Configurations
A build configuration is a named set of build-setting values. Every new project has two:
-
Debug: no optimization (
SWIFT_OPTIMIZATION_LEVEL = -Onone),DEBUGdefined as a compilation condition, testability enabled so tests can use@testable import, and fast incremental builds. -
Release: whole-module optimization (
-O), noDEBUGcondition, debug symbols moved into a separate dSYM for crash symbolication.
Schemes pick one per action, which is why Run is fast to build but Profile measures the code users will
actually get. You can add configurations (a Staging build pointing at a test server is common) in the
project editor’s Info tab. Code that must only exist in Debug uses the DEBUG condition, which is Swift
conditional compilation (see Attributes
and Compiler Control):
extension NoteStore {
func logState() {
#if DEBUG
print("NoteStore: \(notes.count) notes in \(notebooks.count) notebooks")
#endif
}
}
.xcconfig Files for FieldNotes
Build settings edited in the Build Settings tab end up buried in the project file. Build configuration
files (.xcconfig) keep them in plain text instead, where they are easy to review. FieldNotes keeps one
shared base file and one file per configuration, assigned in the project editor’s Info tab under
Configurations:
// Config/Base.xcconfig -- shared by every configuration of every FieldNotes target
MARKETING_VERSION = 2.2.0
CURRENT_PROJECT_VERSION = 42
PRODUCT_BUNDLE_IDENTIFIER = com.example.fieldnotes
IPHONEOS_DEPLOYMENT_TARGET = 26.0
MACOSX_DEPLOYMENT_TARGET = 26.0
WATCHOS_DEPLOYMENT_TARGET = 26.0
XROS_DEPLOYMENT_TARGET = 26.0
SWIFT_VERSION = 6
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor
SWIFT_APPROACHABLE_CONCURRENCY = YES
// The host only: "//" would start a comment in an .xcconfig file, so the scheme lives in Info.plist.
FIELDNOTES_API_HOST = api.fieldnotes.example.com
// Optional, untracked file with each developer's DEVELOPMENT_TEAM (see Getting Started).
#include? "Signing.xcconfig"
// Config/Debug.xcconfig
#include "Base.xcconfig"
SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) DEBUG
SWIFT_OPTIMIZATION_LEVEL = -Onone
ENABLE_TESTABILITY = YES
FIELDNOTES_API_HOST = staging-api.fieldnotes.example.com
// Settings can be conditioned on SDK, architecture or configuration.
SWIFT_ACTIVE_COMPILATION_CONDITIONS[sdk=iphonesimulator*] = $(inherited) DEBUG SIMULATOR
// Config/Release.xcconfig
#include "Base.xcconfig"
SWIFT_OPTIMIZATION_LEVEL = -O
SWIFT_COMPILATION_MODE = wholemodule
DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
$(inherited) appends to the value from the level below instead of replacing it, and #include? includes a
file only if it exists. The Levels view of the Build Settings tab shows where each final value came from.
From lowest to highest precedence, Xcode layers: system defaults, the .xcconfig mapped to the project, the
project’s own settings, the .xcconfig mapped to the target, and the target’s own settings. Values passed to
xcodebuild on the command line override all of them. A value typed into the Build Settings tab therefore
silently beats the same setting in an .xcconfig; teams that adopt .xcconfig files usually delete the
settings from the tab so the file is the single source of truth.
Build Settings That Matter
Xcode has hundreds of build settings; a handful decide most of an app’s behaviour. The first group below is the concurrency set, which decides how strictly the compiler checks data races and what code is main-actor isolated by default. The language rules behind them are in Actors, Isolation and Sendable; how they play out in an app is in Swift Concurrency in Apps.
| Setting | FieldNotes value | Meaning |
|---|---|---|
|
|
The Swift language mode, not the compiler version. Xcode 27 ships the Swift 6.4 compiler, which also compiles Swift 5 mode code. Swift 6 mode turns data-race checks into errors. |
|
(ignored) |
|
|
|
Infers |
|
|
Turns on a bundle of upcoming features, including |
|
|
The app’s unique identity on the system and in App Store Connect; extensions append a suffix
( |
|
|
The user-visible version and the build number, written into |
|
|
The oldest OS the app installs on, per platform; any newer API needs |
|
|
The flags tested by |
The effect of SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor is easiest to see in code. In the FieldNotes app
target, this view model needs no annotation to touch NoteStore (which is itself explicitly @MainActor,
because it lives in a package where the default does not apply), while the import parser explicitly leaves
the main actor:
import Foundation
// Implicitly @MainActor in the app target.
final class ImportController {
let store: NoteStore
init(store: NoteStore) { self.store = store }
func importFile(at url: URL) async throws {
let imported = try await NoteImporter().parse(url) // runs off the main actor
for item in imported { // back on the main actor
store.add(Note(title: item.title, body: item.body, tags: item.tags))
}
}
}
nonisolated struct ImportedNote: Decodable, Sendable {
var title: String
var body: String
var tags: [String]
}
nonisolated struct NoteImporter {
@concurrent
func parse(_ url: URL) async throws -> [ImportedNote] {
let data = try Data(contentsOf: url)
return try JSONDecoder().decode([ImportedNote].self, from: data)
}
}
See Apple Developer Documentation — Build settings reference.
Local packages set the same defaults in Package.swift rather than in build settings, as shown on
Swift Packages and Modularizing an App. The
Decodable conformance that decode relies on is covered in
Codable and Serialization.
Info.plist Values vs Build Settings
Every bundle carries an information property list, Info.plist, which the system reads at install and
launch time: the bundle identifier, the version, the display name, the privacy usage strings, supported
orientations, URL schemes. Build settings, by contrast, are read only by the build. The two meet in three
ways:
-
Generated Info.plist. New targets set
GENERATE_INFOPLIST_FILE = YES, and Xcode writes the plist from build settings namedINFOPLIST_KEY_<Key>. The Info tab of the target editor edits these. -
Variable substitution. A hand-written plist (pointed to by
INFOPLIST_FILE, merged with the generated one) can reference any build setting as$(NAME), which is how a value from an.xcconfigreaches the running app. -
Values from build settings. Some keys are always filled from settings, such as
CFBundleIdentifierfromPRODUCT_BUNDLE_IDENTIFIERandCFBundleShortVersionStringfromMARKETING_VERSION.
FieldNotes uses the generated plist for standard keys and a small plist file for its own:
// Config/Base.xcconfig (continued)
GENERATE_INFOPLIST_FILE = YES
INFOPLIST_FILE = FieldNotes/FieldNotes-Info.plist
INFOPLIST_KEY_CFBundleDisplayName = Field Notes
INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = FieldNotes records where each note was taken.
INFOPLIST_KEY_NSCameraUsageDescription = FieldNotes attaches photos you take to your notes.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>FNAPIBaseURL</key>
<string>https://$(FIELDNOTES_API_HOST)</string>
</dict>
</plist>
At runtime the value comes back from the bundle, already substituted for the configuration that was built:
import Foundation
enum AppConfiguration {
static var apiBaseURL: URL {
guard let string = Bundle.main.object(forInfoDictionaryKey: "FNAPIBaseURL") as? String,
let url = URL(string: string) else {
fatalError("FNAPIBaseURL missing from Info.plist")
}
return url
}
}
The rule of thumb: if the system or the App Store needs to read it, it is an Info.plist key; if only the compiler, linker or signer needs it, it is a build setting; and if the app needs a value that varies by configuration, define it as a build setting and pass it through the plist. Secrets never belong in either, because both end up readable inside the shipped bundle (see Security and Privacy).
The Signing & Capabilities Tab
The Signing & Capabilities tab of each target does two jobs. The Signing section picks the team and
turns on Automatically manage signing, which is enough for development (see the account discussion in
Getting Started with Apple Platforms). The capabilities section adds
features that need Apple’s permission, such as App Groups, iCloud, Push Notifications or HealthKit, with the
+ Capability button. Each capability writes a key into the target’s .entitlements file and, with automatic
signing, registers it on the App ID in the developer account. FieldNotes's app and widget share data through
an App Group:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.example.fieldnotes</string>
</array>
</dict>
</plist>
The file’s path is itself a build setting, CODE_SIGN_ENTITLEMENTS = FieldNotes/FieldNotes.entitlements.
Certificates, provisioning profiles, manual signing and distribution are covered in
Membership, Signing, Capabilities and Info.plist.
Figure: The Build Pipeline
Every Run, Test, Profile and Archive goes through the same pipeline; only the configuration and the signing identity differ. When a build fails, the Report navigator shows which of these steps failed.
SwiftUI Previews
A preview renders one view in the canvas next to the editor, rebuilding only what changed as you type. You
declare it with the #Preview macro (the macro mechanism itself is explained in
Macros); the canvas shows one tab per preview in the file. Show the
canvas with Editor > Canvas (Option-Command-Return). In Xcode 27 code inside #Preview runs on the main actor,
so it can call main-actor APIs such as NoteStore directly. The older PreviewProvider protocol is deprecated
in Xcode 27; convert any remaining ones to #Preview.
Sample Data for FieldNotes
Previews need data that does not come from disk or the network. FieldNotes keeps it next to the model:
extension Note {
static let samples: [Note] = [
Note(title: "Kestrel over the ridge", body: "Hovering at 20 m, 07:40.",
tags: ["birds", "raptors"], isPinned: true),
Note(title: "Spring at the old quarry", body: "Flow noticeably lower than in March.",
tags: ["water"]),
Note(title: "Untitled", body: ""),
]
}
#Preview, Names and Traits
The simplest preview returns a view. A name labels its tab, and preview traits change the context it is
rendered in: .sizeThatFitsLayout for a small component, .landscapeLeft for orientation, .fixedLayout
for an exact size:
import SwiftUI
#Preview("Pinned") {
NoteRow(note: Note.samples[0])
}
#Preview("Row, compact", traits: .sizeThatFitsLayout) {
NoteRow(note: Note.samples[1])
.padding()
}
#Preview("Detail, landscape", traits: .landscapeLeft) {
NavigationStack {
NoteDetail(note: Note.samples[0])
}
}
#Preview("Capture button", traits: .fixedLayout(width: 120, height: 120)) {
CaptureButton { }
}
See Apple Developer Documentation -- Preview(_:traits:_:body:) and Apple Developer Documentation — PreviewTrait.
A view that needs live, mutable state gets it with @Previewable, which declares state inside the preview
without a wrapper view. The editor reads its note from the store, so the preview owns a store seeded with the
sample notes and injects it:
#Preview("Editor") {
@Previewable @State var store = NoteStore(notes: Note.samples)
NoteEditor(noteID: Note.samples[0].id)
.environment(store)
}
Xcode 27 adds argument grids: pass an array with arguments: and the canvas renders one preview per
element, side by side, which is ideal for checking every state of a row at once. Clicking one of them opens it
in interactive mode:
#Preview("Every row state", arguments: Note.samples) { note in
NoteRow(note: note)
.padding()
}
Sharing Expensive Setup with PreviewModifier
Views that read NoteStore from the environment need one injected, and building a realistic store (later, a
SwiftData container) for every preview is slow. A PreviewModifier builds a shared context once, which the
preview system caches and reuses across every preview that applies the modifier through the .modifier trait:
import SwiftUI
struct SampleNotes: PreviewModifier {
static func makeSharedContext() async throws -> NoteStore {
let store = NoteStore()
Note.samples.forEach(store.add)
return store
}
func body(content: Content, context: NoteStore) -> some View {
content.environment(context)
}
}
extension PreviewTrait where T == Preview.ViewTraits {
@MainActor static var sampleNotes: Self { .modifier(SampleNotes()) }
}
#Preview("Split view", traits: .sampleNotes) {
ContentView()
}
#Preview("Note list", traits: .sampleNotes, .landscapeLeft) {
NavigationStack {
NoteList(notebookID: nil, selection: .constant(nil))
}
}
See Apple Developer Documentation — PreviewModifier and Apple Developer Documentation — Previews in Xcode.
When FieldNotes adopts SwiftData, this modifier stays as it is, because views
still read Note values from NoteStore. Only the few screens that use @Query on SwiftData records get a second
preview modifier whose makeSharedContext() returns an in-memory ModelContainer, applied in body with
.modelContainer(context).
Working in the Canvas
The canvas has three modes: Live (the preview is interactive, taps and scrolling work), Selectable (clicking an element selects its code), and a static snapshot. The canvas’s Variants button renders the same preview across color schemes, orientations or Dynamic Type sizes; the overrides picker changes appearance, text size, and (new in Xcode 27) localization, increased contrast and control borders without touching code. iOS previews also get a Resizable Canvas mode in Xcode 27, for checking how a view behaves at arbitrary widths. Previews run in a simulator process for their platform; choose the device in the canvas’s device picker, or My Mac for a macOS preview.
The #Playground Macro
#Playground is the non-UI counterpart of #Preview: a block of code in any Swift file that the canvas runs
and annotates line by line with the resulting values, like a playground document but living next to the code
it exercises. It is ideal for trying out model logic. Import the Playgrounds module, wrap the code, and click
Resume in the canvas:
import Foundation
import Playgrounds
#Playground {
let notes = Note.samples
let tagCounts = Dictionary(grouping: notes.flatMap(\.tags), by: { $0 })
.mapValues(\.count)
let pinned = notes.filter(\.isPinned).map(\.title)
let untitled = notes.filter { $0.title == "Untitled" }.count
}
Playground tabs show a beaker icon and preview tabs an eye icon; in Xcode 27 each tab can be pinned so it stays
visible while you edit another file. Both #Preview and #Playground blocks are compiled out of the shipping
app.
Device Hub
In Xcode 27, Device Hub is the one app that manages every run destination, taking over the jobs of the former Devices and Simulators window. Simulators run inside it, and paired physical devices can be mirrored in it, so an iPhone on the desk can be driven from the Mac. From Device Hub you:
-
create, rename and delete simulators, and install additional simulator runtimes;
-
pair devices by cable or (new with iOS, iPadOS and watchOS 27) over the network with Pair Nearby Device;
-
configure a simulated device’s environment: appearance, text size, location, network conditions;
-
capture screenshots and screen recordings;
-
inspect a device’s identifiers, installed apps and app data containers.
The last point replaces the .xcappdata workflow: download FieldNotes's container from a device, then attach
it to the scheme’s Run action (Options > App Data) to start every debugging session from the same data.
Everything Device Hub does is scriptable through simctl for simulators and devicectl for devices:
# Create a dedicated simulator for FieldNotes UI tests.
xcrun simctl create "FieldNotes iPhone" "iPhone 17" com.apple.CoreSimulator.SimRuntime.iOS-27-0
# Give it a fixed simulated location and dark mode.
xcrun simctl location "FieldNotes iPhone" set 51.5007,-0.1246
xcrun simctl ui "FieldNotes iPhone" appearance dark
# Physical devices: list them and install the app built for one.
xcrun devicectl list devices
xcrun devicectl device install app --device <device-identifier> \
build/Build/Products/Debug-iphoneos/FieldNotes.app
Sidebar: Coding Intelligence and Agents in Xcode 27
Xcode 27 builds coding agents into the IDE. You choose a model or agent in Settings > Intelligence (Claude, OpenAI’s models and Codex, and, new in Xcode 27, Google Gemini), then work in the coding assistant, which now lives in the editor area: conversations run side by side, their artifacts (diffs, plans, preview snapshots) sit next to the transcript, and every change can be reviewed, annotated, undone or rolled back. Useful capabilities for a project like FieldNotes:
-
Plan mode (
/plan): the agent writes an editable Markdown plan that you review and approve before any code changes. -
Built-in specialists for localization, accessibility and UIKit resizing, and a Generate Translations button in the String Catalog editor.
-
Verification: agents can boot simulators, install and launch the app, tap through it, take screenshots and render previews in several variants to check their own work.
-
Generate a preview or playground from a selected symbol through the coding tools popover.
-
Plug-ins adding skills (invoked as slash commands), MCP servers and agent configurations.
Agents that run outside Xcode can use the same tools through Xcode’s MCP server, once Allow external agents to use Xcode tools is turned on in Intelligence settings:
# Give a terminal agent access to the open FieldNotes project through Xcode's MCP bridge.
claude mcp add --transport stdio xcode -- xcrun mcpbridge
codex mcp add xcode -- xcrun mcpbridge
See Apple Developer Documentation — Coding intelligence and Apple Developer Documentation — Giving external agents access to Xcode.
The JSON project format described above exists partly for this reason: agents (and people) can change a
target’s settings in a readable file rather than in project.pbxproj. As with any generated code, review the
diff before committing; the build settings and entitlements an agent touches are exactly the ones this page
describes.
References
-
Apple Developer Documentation — Creating an Xcode project for an app
-
Apple Developer Documentation — Updating your Xcode project configuration file format
-
Apple Developer Documentation — Customizing the build phases of a target
-
Apple Developer Documentation — Running custom scripts during a build
-
Apple Developer Documentation — Customizing the build schemes for a project
-
Apple Developer Documentation — Configuring the build settings of a target
-
Apple Developer Documentation — Adding a build configuration file to your project
-
Apple Developer Documentation — object(forInfoDictionaryKey:)
-
Apple Developer Documentation — Adding capabilities to your app
-
Apple Developer Documentation — Adding previews to your interface files
-
Apple Developer Documentation — Interacting with previews in the canvas
-
Apple Developer Documentation -- Preview(_:traits:arguments:body:)
-
Apple Developer Documentation — Running code snippets using the playground macro
-
Apple Developer Documentation — Managing your simulated and physical devices in Device Hub
-
Apple Developer Documentation — Giving external agents access to Xcode