When should an iOS build reach for build_app's xcargs instead of a first-class option?
answer
- prefer the validated named option
- raw string handed to a shell
- archive and export are two invocations
- permanent settings belong in an xcconfig
- a pile of overrides indicts the project
basics
~20 sOnly when no named option covers it. In fastlane's build_app, xcargs passes raw arguments to Apple's xcodebuild, so prefer validated options such as configuration or sdk, keep project-level settings in an xcconfig, and use export_xcargs for export-only flags.
solid answer
~40 s`build_app` gives you escape hatches of increasing depth: `xcargs`, which appends raw arguments and build-setting overrides to the `xcodebuild` invocation; `export_xcargs`, which does the same for the **export** phase only; `xcconfig`, which points at an Xcode configuration file; and `xcodebuild_command`, which replaces the command entirely. Reach for a named option first — `configuration`, `sdk`, `destination`, `codesigning_identity`, `derived_data_path` — because it is validated, discoverable, and routed to the right phase for you. `xcargs` is right for a genuinely per-invocation value, such as the build number our lighthouse maintenance app takes from the run, or for an `xcodebuild` flag with no fastlane equivalent. When a lane accumulates several permanent overrides, the project configuration is what is wrong, not the lane.
code
ruby · 10 lineslane :build_ios_versioned do
build_app(
scheme: "LighthouseMaintenance",
configuration: "Release",
export_method: "ad-hoc",
xcconfig: "config/ios-release.xcconfig",
xcargs: "CURRENT_PROJECT_VERSION=#{ENV['BUILD_NUMBER']}",
export_xcargs: "-allowProvisioningUpdates"
)
endgo deeper
Know that xcargs exists and passes raw arguments to Apple's xcodebuild, and that you should look for a named build_app option first before reaching for it.
Explain the difference between xcargs and export_xcargs, given that build_app runs archive and export as two separate xcodebuild invocations, and where an xcconfig file fits alongside both.
Diagnose the divergence: an override that makes the automated iOS build behave unlike a local Xcode build is a defect class that reproduces nowhere useful. Say how you would move it back into the project.
Own the boundary between project configuration and lane configuration across the estate, and treat a growing pile of overrides as evidence the project setup is wrong rather than as routine lane maintenance.
## What the escape hatches actually are `build_app` (alias `gym`) exposes dozens of named options, and then several ways out of them for the iOS build it drives: - `xcargs` — a string of raw arguments and build-setting overrides appended to the `xcodebuild` command `build_app` constructs. Anything `xcodebuild` accepts goes here, including `SETTING=value` overrides. - `export_xcargs` — the same, but applied to the **export** invocation only. Because `build_app` runs archive and export as two separate `xcodebuild` calls, a flag that belongs to one and is passed to both is a genuine source of confusing failures. - `xcconfig` — a path to an Xcode configuration file, the format Xcode itself uses for build settings. - `xcodebuild_command` — replaces the command `build_app` shells out to. The deepest hatch, and the one that most reduces what fastlane can reason about. ## Why the named option wins by default When a first-class option exists — `configuration`, `sdk`, `destination`, `toolchain`, `codesigning_identity`, `include_symbols`, `export_team_id`, `derived_data_path` — use it: - It is **validated**, so a mistake fails with a message about the option rather than a wall of `xcodebuild` output starting somewhere inside Apple's toolchain. - It is **discoverable**. The next engineer reading the lane sees an option name they can look up; nobody can look up a fragment of a shell string. - fastlane **knows what it means**, so it can place it in the archive phase, the export phase, or both, correctly, without you having to decide. - `xcargs` is a **single string handed to a shell**, so spaces, quotes and paths inside a value are a recurring, unglamorous source of breakage that only appears on the machine with the awkward directory name. ## When `xcargs` is genuinely the right answer It is an escape hatch, not a smell in itself. Legitimate uses: 1. A build setting `build_app` has no option for at all — the surface is broad but not total. 2. A truly **per-invocation** value: the build number coming from the run, or a compile-time define for one experimental build of our lighthouse maintenance app. 3. An `xcodebuild` flag rather than a build setting, placed into `export_xcargs` when it belongs only to the export. 4. A deliberate one-off you intend to delete, ideally with a comment saying when. ## The judgment a lead is expected to make Before an override goes in, three questions: 1. **Does it describe the project or this invocation?** If the setting is true of the app every time it builds, it belongs in the project or in an `xcconfig` the `xcconfig` option points at, where it is reviewed with the code. If it is true only of this run, `xcargs` is honest. 2. **Which phase owns it?** Export-only flags go to `export_xcargs`. Pushing an export flag through `xcargs` either does nothing or fails in the archive phase for reasons that read as unrelated. 3. **Would an engineer building in Xcode get the same result?** If `xcargs` makes the automated iOS build behave differently from a local one, you have created a class of defect that reproduces nowhere a developer can debug it. That is the expensive failure mode, and it is invisible in review. ## Count as a signal One override is a decision. Five permanent overrides in a single `build_app` call is a message: the project's build configuration is not expressing what the team actually needs, and the lane is compensating for it in a place with no schema, no validation and no review culture. The remedy is to move those settings into the project or an `xcconfig` and delete them from the lane — not to tidy the string. The same logic scales up. Across an estate of apps, overrides in lanes drift independently, so two apps that believe they build the same way quietly do not. Configuration that lives in the project travels with the code, is diffed in review, and is visible to the person opening Xcode. That is the tradeoff being made every time a setting moves into a Ruby string. ## Knowing when to leave `build_app` entirely `xcodebuild_command` replaces the command that is run. It is legitimate when a wrapper genuinely has to sit in front of the compiler — a build-cache or instrumentation tool, for instance. It is illegitimate as a way to avoid learning the option list, because once you own the command you own argument construction, and every `build_app` option that would have shaped that command becomes your problem. If a whole lane has migrated into `xcodebuild_command` and `xcargs`, the useful question is no longer which flag to add next. It is whether this build should be expressed in fastlane at all, or whether the team should own an explicit `xcodebuild` invocation and stop paying for an abstraction it no longer uses. The answer an interviewer wants: escape hatches are correct in proportion to how rare they are, and a lead's job is to keep them rare by fixing whatever made them necessary.
- What breaks when an export-only xcodebuild flag is passed through xcargs instead of export_xcargs?`build_app` runs archive and export as two separate `xcodebuild` invocations, so `xcargs` reaches the archive as well. The archive either ignores the flag or rejects it, and the failure names the archive phase even though the flag was meant for the export. Splitting them keeps each invocation receiving only arguments it understands.
- Where should a build setting that is true of the app on every build live?In the Xcode project or an `xcconfig` file the `xcconfig` option points at, not in `xcargs`. Project configuration is reviewed with the code, travels to anyone opening Xcode locally, and cannot drift between the automated build and the developer build — which is exactly the divergence a permanent `xcargs` override creates.
saying these in an interview costs you the question
- Uses xcargs for settings that have a named build_app option
- Passes export-only flags through xcargs to both phases
- Keeps permanent project settings in a Ruby string
- Reaches for xcodebuild_command to avoid reading the option list
- Ignores that xcargs can make the automated build differ from a local one