skip to content

What does fastlane's build_app action (gym) produce for an iOS app?

level: juniorimportance: must knowfreq 80%

answer

  1. fastlane's wrapper around Apple's xcodebuild
  2. archive first, then export
  3. needs a scheme and an export method
  4. leaves an .ipa plus dSYM bundles
  5. gym is the alias; build_app is canonical

basics

~10 s

fastlane's build_app action, whose alias is gym, wraps Apple's xcodebuild for iOS: it archives the scheme you name, then exports a signed .ipa plus the archive's dSYM debug-symbol bundles into an output directory.

solid answer

~40 s

`build_app` — its famous alias is `gym` — is fastlane's wrapper around Apple's `xcodebuild` for iOS and the other Apple platforms. You give it a `scheme`, either a `workspace` or a `project`, usually a `configuration` such as Release, and an `export_method`. It then runs two Xcode steps: it archives the scheme into an `.xcarchive`, then exports that archive into a distributable package. What lands on disk is a signed `.ipa` in `output_directory` under `output_name`, the `.xcarchive` at `archive_path`, and the dSYM debug-symbol bundles the archive carried. For our lighthouse maintenance app, one `build_app` call turns a checkout into the binary that `upload_to_testflight` (`pilot`) or `upload_to_app_store` (`deliver`) later ships. It is Apple-only — the Android artifact comes from a different build system.

code

ruby · 11 lines
ruby
lane :build_ios do
  build_app(
    workspace: "LighthouseMaintenance.xcworkspace",
    scheme: "LighthouseMaintenance",
    configuration: "Release",
    export_method: "app-store",
    output_directory: "build/ios",
    output_name: "LighthouseMaintenance.ipa"
  )
  UI.message("ipa at #{lane_context[SharedValues::IPA_OUTPUT_PATH]}")
end

go deeper

for a junior

Be ready to name the action and its alias in one breath: build_app, alias gym, wrapping Apple's xcodebuild for iOS. Know the four inputs you always pass - scheme, workspace or project, configuration, export method.

for a middle

Explain the two Xcode phases build_app runs, archive then export, and which options belong to each. Know that dSYM bundles live in the archive and that the exported .ipa alone will not symbolicate a crash.

for a senior

Show where build_app sits between sync_code_signing upstream and upload_to_testflight downstream, and how the lane context passes the .ipa path along rather than a hardcoded filename you must keep in sync.

for a principal

Own the seam: one archive can serve several exports, so decide whether the organisation rebuilds per destination or exports once per archive, and say what that means for artifact retention and reproducibility.

## What `build_app` actually is `build_app` is a built-in **fastlane action** — one of the steps you call from a lane in a `Fastfile`. Its famous short name `gym` is an **alias**: both spellings run the same code, and `build_app` is the canonical name to use in new lanes and to say out loud in an interview. Its single job is to drive Apple's `xcodebuild` command-line tool so that a checkout of an iOS project becomes a signed, distributable package with nobody opening Xcode. It is **Apple-only**. There is no Android path through `build_app`; an Android artifact comes out of the Android build system, which is a different tool and a different lane. Talking about "the build lane" without naming the platform is the fastest way to sound like you have only skimmed the README. ## The inputs it cannot guess `build_app` carries dozens of options, but a handful actually decide the outcome: - `scheme` — the Xcode **scheme** to build. A scheme says which targets compile, which configuration each action uses, and which target is archived. A project holding an app, a widget extension and a UI-test target gives `build_app` nothing safe to guess from, so name it. - `workspace` or `project` — which Xcode container to open. Use `workspace` when a dependency manager generated one, `project` for a bare `.xcodeproj`. - `configuration` — the Xcode build configuration. `Release` for anything you distribute; `Debug` only for a local smoke build. - `export_method` — which Apple distribution channel the exported package is signed for. - `output_directory` and `output_name` — where the package lands and what it is called. Everything else — `clean`, `sdk`, `destination`, `include_symbols`, `xcargs` — refines a build that already works. ## Two Xcode phases, not one `build_app` invokes Apple's toolchain twice, and knowing that seam is what separates a junior answer from a middle one. 1. **Archive.** It runs Xcode's archive action for your `scheme` and `configuration`, producing an `.xcarchive` bundle at `archive_path`. The archive holds the built iOS app **and** its debug-symbol (`dSYM`) bundles — the files that later turn a stack of hexadecimal addresses in a crash report back into function names and line numbers. 2. **Export.** It then runs Xcode's export step over that archive with an export-options property list, signing and repackaging the app for exactly one destination. This is where `export_method` and `export_options` apply. You can stop after either phase. `skip_archive` and `skip_build_archive` skip ahead when an archive already exists; `skip_package_ipa` archives without exporting, which is what a compile-only pull-request check on our lighthouse maintenance app wants; `skip_codesigning` archives an unsigned iOS app for the same reason. ## What lands on disk | Artifact | Controlled by | Consumed by | |---|---|---| | Signed `.ipa` | `output_directory`, `output_name` | `upload_to_testflight` (`pilot`), `upload_to_app_store` (`deliver`) | | `.xcarchive` | `archive_path` | a later re-export, and symbol recovery | | dSYM bundles | carried in the archive; `include_symbols` for the export | your crash reporter | | Raw `xcodebuild` log | `buildlog_path` | you, when it fails | | `.xcresult` bundle | `result_bundle`, `result_bundle_path` | build report viewers | For a macOS target the packaged artifact is a `.pkg` rather than an `.ipa`, and `skip_package_pkg` is its skip switch. ## How the next action finds the binary `build_app` returns the path of the package it built and also publishes it into fastlane's shared lane context: `Actions.lane_context[SharedValues::IPA_OUTPUT_PATH]` holds the `.ipa` path and `SharedValues::PKG_OUTPUT_PATH` the macOS `.pkg` path. That is why a lane can call `build_app` and then `upload_to_testflight` with no filename written between them — the second action reads the first action's output out of the context. It is also why hardcoding a path is a smell: change `output_name` and the hardcoded string rots, while the context value does not. ## What `build_app` is deliberately not - It does **not** create or fetch signing assets. Apple certificates and provisioning profiles come from `sync_code_signing` (`match`); `build_app` only consumes what is already on the machine. - It does **not** upload anything. Shipping the binary is `upload_to_testflight` (`pilot`) for Apple's beta service and `upload_to_app_store` (`deliver`) for the store listing. - It does **not** build for Google Play. Nothing inside `build_app` touches an Android artifact. Keeping those three seams straight is most of what an interviewer is checking. The candidate who answers "gym builds and ships it" has merged three lanes into one, and will not know where to look on the day only the upload fails.

  • Where does the next action in the lane find the .ipa that build_app just produced?
    `build_app` returns the path and also publishes it into fastlane's lane context, so `Actions.lane_context[SharedValues::IPA_OUTPUT_PATH]` holds the `.ipa` path and `SharedValues::PKG_OUTPUT_PATH` the macOS `.pkg` path. That is how `upload_to_testflight` (`pilot`) picks the binary up with no filename written in between. Hardcoding `output_directory` plus `output_name` works too, but rots the moment either changes.
  • Can build_app run without doing any code signing at all?
    Yes. `skip_codesigning: true` archives the iOS app unsigned, which is what a compile-only pull-request check on our lighthouse maintenance app wants. You get no distributable `.ipa` from that run, because the export phase is what signs and packages, so pair it with `skip_package_ipa` when you only need to know the code still compiles.

saying these in an interview costs you the question

  • Thinks gym also builds the Android artifact
  • Only ever says gym, never the canonical build_app
  • Believes build_app uploads the binary to Apple's beta service
  • Expects an .ipa without naming a scheme
  • Confuses the .xcarchive with the exported .ipa
  • Thinks build_app creates the provisioning profile it signs with