Launch arguments let an XCUITest run start the app under test with a test-only mode, fixture choice, or feature switch. The app still owns the behavior change, while the UI test controls which startup values are present for that run.
XCUIApplication passes launchArguments and launchEnvironment into the application process when launch() starts it. Read those values from ProcessInfo.processInfo in the app target, not from the UI test target after the app is already running.
Use arguments for boolean startup flags and environment values for named fixtures or short non-secret strings. Do not pass credentials, tokens, or customer identifiers this way because launch configuration can appear in test logs, reports, or debugging output.
import Foundation struct UITestLaunchOptions { static let testModeArgument = "-UITestMode" static let fixtureEnvironmentKey = "UITEST_FIXTURE" let isEnabled: Bool let fixtureName: String? static var current: UITestLaunchOptions { let process = ProcessInfo.processInfo return UITestLaunchOptions( isEnabled: process.arguments.contains(testModeArgument), fixtureName: process.environment[fixtureEnvironmentKey] ) } }
launchArguments are available through ProcessInfo.processInfo.arguments. launchEnvironment values are available through ProcessInfo.processInfo.environment.
import SwiftUI @main struct MyApp: App { private let launchOptions = UITestLaunchOptions.current var body: some Scene { WindowGroup { LoginView( useFixtureData: launchOptions.isEnabled, fixtureName: launchOptions.fixtureName ?? "default" ) } } }
For UIKit app delegates, read the same helper near the start of application(_:didFinishLaunchingWithOptions:). Values set after app.launch() are not applied to the already-running app.
import SwiftUI struct LoginView: View { let useFixtureData: Bool let fixtureName: String var body: some View { VStack { if useFixtureData { Text("Using UI test fixture") .accessibilityIdentifier("fixture-banner") Text(fixtureName) .accessibilityIdentifier("fixture-name") } Button("Sign In") { signIn(usingFixture: useFixtureData ? fixtureName : nil) } .accessibilityIdentifier("sign-in-button") } } }
The proof can be a screen identifier, seeded account, mock service, disabled animation state, or another app-owned result. Keep the assertion tied to the startup value that the test sets.
import XCTest final class LoginUITests: XCTestCase { func testLoginUsesUITestFixture() { let app = XCUIApplication() app.launchArguments.append("-UITestMode") app.launchEnvironment["UITEST_FIXTURE"] = "login-success" app.launch() XCTAssertTrue(app.staticTexts["fixture-banner"].waitForExistence(timeout: 5)) XCTAssertEqual(app.staticTexts["fixture-name"].label, "login-success") } }
Set launchArguments and launchEnvironment on the XCUIApplication instance that will launch the app. Relaunch the app when a later test needs a different startup mode.
$ xcodebuild test \ -workspace MyApp.xcworkspace \ -scheme MyApp \ -destination 'platform=iOS Simulator,name=iPhone 16,OS=latest' \ -only-testing:MyAppUITests/LoginUITests/testLoginUsesUITestFixture Test Suite 'LoginUITests' passed Executed 1 test, with 0 failures (0 unexpected) ** TEST SUCCEEDED **
A passing run proves the UI test supplied the launch values and the app changed startup behavior only for that test run. Use the real workspace, scheme, simulator name, UI test target, class, and method from the project.