
Xcuitest
- 367 installs
- 297 repo stars
- Updated August 4, 2026
- vabole/apple-skills
xcuitest is a Claude Code reference skill that documents XCUITest UI automation APIs—including Swift 6 @MainActor patterns, accessibility queries, waiting mechanisms, and launch arguments—for iOS developers authoring CI-
About
xcuitest is a vabole/apple-skills comprehensive reference for writing reliable XCUITest UI tests in Swift 6 with @MainActor test classes. Coverage spans XCUIApplication lifecycle (launchArguments, launchEnvironment, terminate), XCUIElementQuery patterns (buttons, predicates, chained descendants), interaction APIs (tap, swipe, pinch, typeText), and waiting via waitForExistence, XCTWaiter, and Xcode 16+ waitForNonExistence. Includes launch-argument recipes for disabling animations, stubbing API URLs, and resetting state between tests plus screenshot and attachment helpers for CI artifacts. The forked Explore agent context suits lookup during test authoring. Reach for xcuitest when building iOS UI test suites, debugging flaky element waits, or configuring CI-friendly XCUITest runs on simulators and devices.
- XCUITest target and scheme setup
- UI query and accessibility identifiers
- Launch arguments and test data seeding
- Flake reduction and wait strategies
- CI integration for iOS simulators
Xcuitest by the numbers
- 367 all-time installs (skills.sh)
- +5 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #663 of 2,153 Testing & QA skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/vabole/apple-skills --skill xcuitestAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 367 |
|---|---|
| repo stars | ★ 297 |
| Last updated | August 4, 2026 |
| Repository | vabole/apple-skills ↗ |
How do you write reliable XCUITest UI tests?
Author and maintain XCUITest UI automation for iOS apps, including launch flows, accessibility queries, and CI-friendly test suites.
Who is it for?
iOS developers authoring or debugging XCUITest UI automation with Swift 6, accessibility queries, and simulator or device CI pipelines.
Skip if: Skip xcuitest for Android Espresso tests, unit tests without UI interaction, or SwiftUI preview-only validation without XCUITest infrastructure.
When should I use this skill?
User writes XCUITest tests, debugs flaky XCUIElement waits, configures launchArguments, or asks about accessibility queries and @MainActor test structure.
What you get
XCUITest test classes with element queries, wait patterns, launch-argument configuration, assertions, and CI-ready screenshot attachments.
- XCUITest test classes
- Launch-argument configuration
- CI-friendly wait patterns
By the numbers
- Comprehensive API reference covering XCUIApplication, XCUIElement, and XCUIElementQuery
- Documents Xcode 16+ waitForNonExistence and Xcode 26+ KeyPath-based property waiting
Files
XCUITest Reference
Comprehensive reference for writing reliable XCUITest UI tests in Swift 6.
Quick Reference
// Basic test structure
@MainActor
final class MyUITests: XCTestCase {
var app: XCUIApplication!
override func setUp() {
super.setUp()
continueAfterFailure = false
app = XCUIApplication()
app.launch()
}
func testExample() {
let button = app.buttons["Submit"]
XCTAssertTrue(button.waitForExistence(timeout: 5))
button.tap()
}
}Core API Classes
XCUIApplication
Proxy for launching, monitoring, and terminating the app under test.
let app = XCUIApplication()
// Launch configuration
app.launchArguments = ["-UITest", "-DisableAnimations"]
app.launchEnvironment["API_URL"] = "https://test.example.com"
// Lifecycle
app.launch() // Start the app
app.terminate() // Stop the app
app.activate() // Bring to foreground
// State checking
app.state == .runningForeground
app.state == .runningBackground
app.state == .notRunningXCUIElement
Represents a single UI element. Supports interactions and property queries.
let element = app.buttons["Submit"]
// Properties
element.exists // Bool - element is in hierarchy
element.isHittable // Bool - element can receive taps
element.isEnabled // Bool - element is enabled
element.isSelected // Bool - element is selected
element.label // String - accessibility label
element.value // Any? - current value
element.identifier // String - accessibility identifier
element.frame // CGRect - frame in screen coordinates
element.elementType // XCUIElement.ElementTypeXCUIElementQuery
Defines search criteria for finding UI elements.
// Type-based queries (convenience)
app.buttons // All buttons
app.staticTexts // All text labels
app.textFields // All text inputs
app.secureTextFields // Password fields
app.switches // Toggle switches
app.sliders // Slider controls
app.tables // Table views
app.cells // Table/collection cells
app.scrollViews // Scroll views
app.images // Image views
app.alerts // Alert dialogs
app.sheets // Action sheets
app.navigationBars // Navigation bars
app.tabBars // Tab bars
app.toolbars // Toolbars
// Querying by identifier (subscript)
app.buttons["Submit"]
app.staticTexts["Welcome"]
// Descendants query (any element type)
app.descendants(matching: .any)
app.descendants(matching: .button)
app.descendants(matching: .staticText)
// Chained queries
app.descendants(matching: .any).matching(identifier: "my-id").firstMatch
// Predicate queries
app.buttons.matching(NSPredicate(format: "label CONTAINS[c] 'Save'"))
app.buttons.matching(NSPredicate(format: "identifier == 'submit-btn'"))
app.staticTexts.matching(NSPredicate(format: "label BEGINSWITH 'Error'"))
// Query results
query.count // Number of matches
query.element // Single element (fails if not exactly 1)
query.firstMatch // First matching element
query.element(boundBy: 0) // Element at index
query.allElementsBoundByIndex // Array of all elementsXCUICoordinate
Represents a screen location for coordinate-based interactions.
// Normalized offset (0,0 = top-left, 1,1 = bottom-right)
let center = element.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5))
let topLeft = element.coordinate(withNormalizedOffset: CGVector(dx: 0, dy: 0))
// Absolute offset from normalized point
let point = app.coordinate(withNormalizedOffset: .zero)
.withOffset(CGVector(dx: 100, dy: 200))
// Screen coordinates
let screenCenter = app.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5))Element Interactions
Tap Actions
element.tap() // Single tap
element.doubleTap() // Double tap
element.twoFingerTap() // Two finger tap (iOS only)
element.tap(withNumberOfTaps: 3, numberOfTouches: 1) // Triple tapPress Actions
element.press(forDuration: 1.0) // Long press
element.press(forDuration: 0.5, thenDragTo: otherElement) // Press and dragText Input
textField.tap() // Focus first
textField.typeText("Hello") // Type text
textField.clearAndEnterText("New text") // Custom helper needed
// Clear text field
textField.tap()
textField.press(forDuration: 1.0)
app.menuItems["Select All"].tap()
textField.typeText("") // Or use delete keySwipe Gestures
element.swipeUp()
element.swipeDown()
element.swipeLeft()
element.swipeRight()
// With velocity (iOS 16+)
element.swipeUp(velocity: .fast)
element.swipeUp(velocity: .slow)Coordinate-Based Gestures
// Pull to refresh
let start = cell.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0))
let end = cell.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 6))
start.press(forDuration: 0, thenDragTo: end)
// Custom swipe
let from = app.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.8))
let to = app.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.2))
from.press(forDuration: 0.1, thenDragTo: to)
// Tap at specific point
let point = app.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5))
point.tap()Other Gestures
element.pinch(withScale: 0.5, velocity: -1) // Pinch in
element.pinch(withScale: 2.0, velocity: 1) // Pinch out
element.rotate(0.5, withVelocity: 1) // Rotate
// Sliders
slider.adjust(toNormalizedSliderPosition: 0.7)
// Pickers
picker.adjust(toPickerWheelValue: "Option 3")Waiting Mechanisms
waitForExistence (Simplest)
// Returns Bool - does not fail test automatically
let exists = element.waitForExistence(timeout: 5)
XCTAssertTrue(exists, "Element did not appear")
// Common pattern
if button.waitForExistence(timeout: 3) {
button.tap()
}XCTWaiter (More Control)
// Wait with result handling
let predicate = NSPredicate(format: "exists == true")
let expectation = XCTNSPredicateExpectation(predicate: predicate, object: element)
let result = XCTWaiter().wait(for: [expectation], timeout: 5)
switch result {
case .completed:
// Element found
case .timedOut:
XCTFail("Element did not appear within timeout")
case .incorrectOrder:
// Multiple expectations fulfilled out of order
case .invertedFulfillment:
// Inverted expectation was fulfilled (unexpected)
case .interrupted:
// Wait was interrupted
@unknown default:
break
}Wait for Non-Existence (Xcode 16+)
// Native API (Xcode 16+) - preferred
let loadingIndicator = app.activityIndicators["loading"]
XCTAssertTrue(loadingIndicator.waitForNonExistence(withTimeout: 10), "Loading should complete")
// Legacy approach (pre-Xcode 16)
func waitForNonExistence(_ element: XCUIElement, timeout: TimeInterval) -> Bool {
let predicate = NSPredicate(format: "exists == false")
let expectation = XCTNSPredicateExpectation(predicate: predicate, object: element)
let result = XCTWaiter().wait(for: [expectation], timeout: timeout)
return result == .completed
}Wait for Property Change
// Wait for element to become enabled
let predicate = NSPredicate(format: "isEnabled == true")
let expectation = XCTNSPredicateExpectation(predicate: predicate, object: button)
XCTWaiter().wait(for: [expectation], timeout: 5)
// Wait for label to change
let predicate = NSPredicate(format: "label == 'Done'")
let expectation = XCTNSPredicateExpectation(predicate: predicate, object: statusLabel)
XCTWaiter().wait(for: [expectation], timeout: 10)Multiple Expectations
let exp1 = XCTNSPredicateExpectation(predicate: pred1, object: element1)
let exp2 = XCTNSPredicateExpectation(predicate: pred2, object: element2)
XCTWaiter().wait(for: [exp1, exp2], timeout: 10, enforceOrder: false)Wait for Property Value (Xcode 26+ / iOS 26+)
// New KeyPath-based waiting - wait for any property to equal a value
let favoriteButton = app.buttons["Favorite"]
favoriteButton.tap()
// Wait for value property to become true
XCTAssertTrue(
favoriteButton.wait(for: \.value, toEqual: true, timeout: 10),
"Button should show favorited state"
)
// Wait for label to change
XCTAssertTrue(
statusLabel.wait(for: \.label, toEqual: "Complete", timeout: 5),
"Status should update to Complete"
)
// Wait for element to become enabled
XCTAssertTrue(
submitButton.wait(for: \.isEnabled, toEqual: true, timeout: 3),
"Submit button should become enabled"
)Note: The wait(for:toEqual:timeout:) method uses Swift KeyPaths for type-safe property access. It returns true if the property matches the expected value within the timeout, false otherwise.
Permission Handling
Reset Authorization Status
Reset permissions before tests to ensure consistent state. Call before app.launch().
override func setUp() {
super.setUp()
let app = XCUIApplication()
// Reset permissions before launch
app.resetAuthorizationStatus(for: .location)
app.resetAuthorizationStatus(for: .camera)
app.resetAuthorizationStatus(for: .photos)
app.resetAuthorizationStatus(for: .health)
app.launch()
}XCUIProtectedResource Types
// Available protected resources
.contacts // Contacts access
.calendar // Calendar access
.reminders // Reminders access
.photos // Photo library access
.microphone // Microphone access
.camera // Camera access
.mediaLibrary // Media library access
.homeKit // HomeKit access
.bluetooth // Bluetooth access
.keyboardNetwork // Network keyboard access
.location // Location services
.health // HealthKit accessHandling HealthKit Permission Dialog (iOS 26)
HealthKit authorization on iOS 26 uses a scrollable sheet with buttons below the fold:
func handleHealthKitDialog(allow: Bool = false) {
let healthAccessText = app.staticTexts["Health Access"]
if healthAccessText.waitForExistence(timeout: 5) {
// Scroll down to reveal buttons (iOS 26 sheet is scrollable)
let from = app.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.8))
let to = app.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.3))
from.press(forDuration: 0.1, thenDragTo: to)
// Tap the appropriate button
let buttonLabel = allow ? "Allow" : "Don't Allow"
let button = app.buttons[buttonLabel]
if button.waitForExistence(timeout: 5) {
button.tap()
} else {
// Fallback: find by partial match
let fallback = app.buttons.matching(
NSPredicate(format: "label CONTAINS[c] '\(buttonLabel)'")
).firstMatch
if fallback.exists { fallback.tap() }
}
}
}Using UI Interruption Monitor
For handling unexpected permission dialogs during tests:
override func setUp() {
super.setUp()
// Handle location permission
addUIInterruptionMonitor(withDescription: "Location Permission") { alert -> Bool in
if alert.buttons["Allow While Using App"].exists {
alert.buttons["Allow While Using App"].tap()
return true
}
if alert.buttons["Don't Allow"].exists {
alert.buttons["Don't Allow"].tap()
return true
}
return false
}
// Handle HealthKit permission
addUIInterruptionMonitor(withDescription: "Health Permission") { alert -> Bool in
// Note: HealthKit uses a sheet, not a system alert
// This may not trigger the interruption monitor
return false
}
app.launch()
}
func testWithPermissions() {
// Trigger action that shows permission dialog
app.buttons["Enable Location"].tap()
// IMPORTANT: Must interact with app to trigger the monitor
app.tap()
// Continue test...
}Springboard Alert Handling
For system-level alerts not caught by interruption monitor:
func handleSpringboardAlert(buttonLabel: String) {
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
let alertButton = springboard.buttons[buttonLabel]
if alertButton.waitForExistence(timeout: 3) {
alertButton.tap()
}
}
// Usage
handleSpringboardAlert(buttonLabel: "Allow")
handleSpringboardAlert(buttonLabel: "Don't Allow")Swift 6 Concurrency
The Problem
Swift 6 strict concurrency requires proper actor isolation. XCTestCase methods are not main-actor-isolated by default, causing errors when accessing @MainActor objects.
Error you'll see:
Call to main actor-isolated initializer 'init()' in a synchronous nonisolated contextSolution 1: Mark Test Class with @MainActor (Recommended)
@MainActor
final class MyUITests: XCTestCase {
var app: XCUIApplication!
override func setUp() {
super.setUp()
app = XCUIApplication()
app.launch()
}
override func tearDown() {
app = nil
super.tearDown()
}
func testSomething() {
// All code runs on main actor
}
}Solution 2: Async setUp/tearDown with MainActor.run
final class MyUITests: XCTestCase {
var app: XCUIApplication!
override func setUp() async throws {
try await super.setUp()
await MainActor.run {
app = XCUIApplication()
app.launch()
}
}
override func tearDown() async throws {
await MainActor.run {
app = nil
}
try await super.tearDown()
}
}Solution 3: Mark Properties as nonisolated
For properties that don't need main actor:
@MainActor
final class MyUITests: XCTestCase {
nonisolated var testUserID: String {
ProcessInfo.processInfo.environment["TEST_USER_ID"] ?? "default"
}
}Async Test Methods
@MainActor
func testAsyncOperation() async throws {
app.buttons["Start"].tap()
// Await async operation
try await Task.sleep(nanoseconds: 1_000_000_000)
XCTAssertTrue(app.staticTexts["Complete"].exists)
}Screenshots and Attachments
Take Screenshot
// Screenshot of entire screen
let screenshot = XCUIScreen.main.screenshot()
// Screenshot of specific element
let elementShot = element.screenshot()
// Create attachment
let attachment = XCTAttachment(screenshot: screenshot)
attachment.name = "Login Screen"
attachment.lifetime = .keepAlways // Don't delete after test
add(attachment)Automatic Screenshot on Failure
override func tearDown() {
if let failureCount = testRun?.failureCount, failureCount > 0 {
let screenshot = XCUIScreen.main.screenshot()
let attachment = XCTAttachment(screenshot: screenshot)
attachment.name = "Failure-\(name)"
attachment.lifetime = .keepAlways
add(attachment)
}
super.tearDown()
}Access Screenshots After Test
Screenshots are stored in the .xcresult bundle in DerivedData. Use xcparse to extract:
brew install xcparse
xcparse screenshots /path/to/Test.xcresult /output/directoryLaunch Arguments and Environment
Setting Values
let app = XCUIApplication()
// Launch arguments (appear in ProcessInfo.processInfo.arguments)
app.launchArguments = ["-UITest", "-DisableAnimations"]
app.launchArguments.append("-SkipOnboarding")
// Launch environment (appear in ProcessInfo.processInfo.environment)
app.launchEnvironment["API_URL"] = "https://test.example.com"
app.launchEnvironment["TEST_USER_ID"] = "test-user-123"
app.launch() // Must be set before launch!Reading in App Code
// Check for launch argument
if ProcessInfo.processInfo.arguments.contains("-UITest") {
UIView.setAnimationsEnabled(false)
}
// Read environment variable
if let testUserID = ProcessInfo.processInfo.environment["TEST_USER_ID"] {
UserDefaults.standard.set(testUserID, forKey: "testUserID")
}UserDefaults Override Trick
Arguments with - prefix set UserDefaults:
// In test
app.launchArguments = ["-myKey", "myValue"]
// In app - reads from UserDefaults
UserDefaults.standard.string(forKey: "myKey") // "myValue"Localization Testing
app.launchArguments = [
"-AppleLanguages", "(es)",
"-AppleLocale", "es_ES"
]Accessibility Testing
app.launchArguments = [
"-UIPreferredContentSizeCategoryName",
UIContentSizeCategory.accessibilityExtraExtraExtraLarge.rawValue
]Assertions
Basic Assertions
XCTAssertTrue(element.exists)
XCTAssertFalse(element.exists)
XCTAssertEqual(element.label, "Expected")
XCTAssertNotEqual(element.value as? String, "Wrong")
XCTAssertNil(element.value)
XCTAssertNotNil(element.value)With Custom Messages
XCTAssertTrue(element.exists, "Submit button should be visible")
XCTAssertEqual(element.label, "Done", "Button label should be 'Done' after completion")Existence Patterns
// Element must exist (fails test if not)
XCTAssertTrue(element.waitForExistence(timeout: 5), "Element not found")
// Element should not exist
XCTAssertFalse(app.alerts["Error"].exists, "Unexpected error alert")
// Element state
XCTAssertTrue(button.isEnabled, "Button should be enabled")
XCTAssertTrue(button.isHittable, "Button should be tappable")System Alerts
Interruption Monitor (for system dialogs)
override func setUp() {
super.setUp()
// Set up before launching app
addUIInterruptionMonitor(withDescription: "System Alert") { alert -> Bool in
// Handle location permission
if alert.buttons["Allow While Using App"].exists {
alert.buttons["Allow While Using App"].tap()
return true
}
// Handle notification permission
if alert.buttons["Allow"].exists {
alert.buttons["Allow"].tap()
return true
}
// Handle "Don't Allow"
if alert.buttons["Don't Allow"].exists {
alert.buttons["Don't Allow"].tap()
return true
}
return false
}
app.launch()
}
func testWithSystemAlert() {
// Trigger action that shows system alert
app.buttons["Request Permission"].tap()
// IMPORTANT: Must interact with app to trigger handler
app.tap()
// Continue test...
}Direct Alert Handling
// For app alerts (not system)
let alert = app.alerts["Confirm Delete"]
if alert.waitForExistence(timeout: 3) {
alert.buttons["Delete"].tap()
}
// For springboard alerts (system)
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
let systemAlert = springboard.alerts.firstMatch
if systemAlert.waitForExistence(timeout: 3) {
systemAlert.buttons["Allow"].tap()
}Common Patterns
Page Object Model
protocol Screen {
var app: XCUIApplication { get }
func waitForScreen(timeout: TimeInterval) -> Bool
}
struct LoginScreen: Screen {
let app: XCUIApplication
var usernameField: XCUIElement { app.textFields["username"] }
var passwordField: XCUIElement { app.secureTextFields["password"] }
var loginButton: XCUIElement { app.buttons["Login"] }
var errorLabel: XCUIElement { app.staticTexts["error-message"] }
func waitForScreen(timeout: TimeInterval = 5) -> Bool {
usernameField.waitForExistence(timeout: timeout)
}
func login(username: String, password: String) -> HomeScreen {
usernameField.tap()
usernameField.typeText(username)
passwordField.tap()
passwordField.typeText(password)
loginButton.tap()
return HomeScreen(app: app)
}
}Reusable Helpers
extension XCUIApplication {
func waitForElement(_ identifier: String, timeout: TimeInterval = 5) -> XCUIElement {
let element = descendants(matching: .any).matching(identifier: identifier).firstMatch
XCTAssertTrue(element.waitForExistence(timeout: timeout),
"Element '\(identifier)' not found within \(timeout)s")
return element
}
func tapTab(_ identifier: String) {
let tab = buttons[identifier]
XCTAssertTrue(tab.waitForExistence(timeout: 5), "Tab '\(identifier)' not found")
tab.tap()
}
}
extension XCUIElement {
func clearAndType(_ text: String) {
guard let currentValue = value as? String, !currentValue.isEmpty else {
tap()
typeText(text)
return
}
tap()
let deleteString = String(repeating: XCUIKeyboardKey.delete.rawValue, count: currentValue.count)
typeText(deleteString)
typeText(text)
}
}Timeout Constants
enum TestTimeout: TimeInterval {
case short = 2
case medium = 5
case long = 10
case networkLoad = 30
}
// Usage
button.waitForExistence(timeout: TestTimeout.medium.rawValue)Advanced Patterns
For more detailed patterns including:
- Base test class implementation
- iOS system dialog handling (HealthKit, Location, Notifications)
- Scroll and wait patterns
- Text field helpers
- Debugging techniques
- Test organization
See patterns.md.
Troubleshooting
Element Not Found
1. Check accessibility identifier is set in code 2. Use Accessibility Inspector (Xcode > Open Developer Tool) 3. Print element hierarchy: print(app.debugDescription) 4. Check element is in view (may need scrolling) 5. Ensure element is enabled for accessibility
Flaky Tests
1. Use waitForExistence instead of sleep 2. Add continueAfterFailure = false 3. Disable animations in test setup 4. Reset simulator state between tests 5. Use unique test data
Swift 6 Concurrency Errors
1. Mark test class with @MainActor 2. Use nonisolated for properties that don't need main actor 3. Use async setUp/tearDown if needed 4. Check for Sendable conformance issues
Screenshots Not Saved
1. Set attachment.lifetime = .keepAlways 2. Check DerivedData location for .xcresult 3. Use xcparse to extract from result bundle
Permission Dialog Issues
1. Use resetAuthorizationStatus(for:) before app.launch() 2. HealthKit uses a scrollable sheet on iOS 26 - must scroll to reveal buttons 3. addUIInterruptionMonitor requires an app interaction to trigger 4. For system-level alerts, use springboard bundle identifier approach
What's New in Xcode 26 / iOS 26
New APIs
- `wait(for:toEqual:timeout:)` - KeyPath-based waiting for any property value
- `waitForNonExistence(withTimeout:)` - Native API to wait for element removal (Xcode 16+)
- XCTHitchMetric - Measure UI responsiveness and frame drops
Enhanced Recording
- Cleaner, more maintainable generated test code
- Multiple identifier options for element selection
- Integration with Test Report's Automation Explorer
Cross-Platform Testing
- Run automation tests across iPhone, iPad, Mac, Apple TV, Apple Watch
- Test plan configurations for multiple locales, device types, system conditions
- Video recordings and screenshots of test runs in results
Swift Concurrency Debugging
- Seamless stepping across threads in async tests
- Task ID visibility in debugger
- Thread Performance Checker integration
Sources
Apple Documentation
- XCTest | Apple Developer Documentation
- XCUIAutomation | Apple Developer Documentation
- XCUIElementQuery | Apple Developer Documentation
- waitForExistence | Apple Developer Documentation
- wait(for:toEqual:timeout:) | Apple Developer Documentation)
- resetAuthorizationStatus(for:) | Apple Developer Documentation
- XCUIProtectedResource | Apple Developer Documentation
- XCUIScreenshot | Apple Developer Documentation
WWDC Sessions
Community Resources
- XCTest Meets @MainActor | Quality Coding
- Issues with setUp() and tearDown() in XCTest for Swift 6 | Swift Forums
- Waiting in XCTest | Masilotti.com
- UI Testing Cheat Sheet | Masilotti.com
- XCUIElement Actions and Gestures | Apps Developer Blog
- Configuring UI tests with launch arguments | polpiella.dev
- UI Testing improvements in Xcode 16 | Jesse Squires
Fetching More Apple Docs
1. Search this skill's local .md files first. 2. If the topic is not here, check the other installed Apple skills you have available by their names, descriptions, or SKILL.md frontmatter, then grep their local files. This is faster and uses less context than fetching new docs from the internet. 3. If no installed skill has the page, use the relevant documentation path from the local XCTest or XCUIAutomation indexes with the sosumi.ai Markdown mirror. For example, /documentation/xcuiautomation/xcuielement maps to https://sosumi.ai/documentation/xcuiautomation/xcuielement.
XCUITest Advanced Patterns
Base Test Class Pattern
A reusable base class that handles common setup, teardown, and helpers:
import XCTest
@MainActor
class BaseUITest: XCTestCase {
var app: XCUIApplication!
// Configuration from environment
nonisolated var apiURL: String {
ProcessInfo.processInfo.environment["API_URL"] ?? "https://api.example.com"
}
nonisolated var testUserID: String {
ProcessInfo.processInfo.environment["TEST_USER_ID"] ?? "default-user"
}
override func setUp() {
super.setUp()
continueAfterFailure = false
app = XCUIApplication()
app.terminate() // Ensure fresh state
// Pass configuration
app.launchArguments += ["-UITest"]
app.launchArguments += ["-API_URL", apiURL]
app.launchArguments += ["-TEST_USER_ID", testUserID]
app.launch()
}
override func tearDown() {
captureScreenshotOnFailure()
app = nil
super.tearDown()
}
// MARK: - Helpers
func captureScreenshotOnFailure() {
guard let failureCount = testRun?.failureCount, failureCount > 0 else { return }
let screenshot = XCUIScreen.main.screenshot()
let attachment = XCTAttachment(screenshot: screenshot)
attachment.name = "Failure-\(name)"
attachment.lifetime = .keepAlways
add(attachment)
}
func waitForElement(_ identifier: String, timeout: TimeInterval = 10) -> XCUIElement {
let element = app.descendants(matching: .any).matching(identifier: identifier).firstMatch
XCTAssertTrue(element.waitForExistence(timeout: timeout),
"Element '\(identifier)' did not appear within \(timeout)s")
return element
}
func takeScreenshot(_ name: String) {
let screenshot = XCUIScreen.main.screenshot()
let attachment = XCTAttachment(screenshot: screenshot)
attachment.name = name
attachment.lifetime = .keepAlways
add(attachment)
}
func tapTab(_ identifier: String) {
let tab = app.buttons[identifier]
if tab.waitForExistence(timeout: 2) {
tab.tap()
Thread.sleep(forTimeInterval: 0.3) // Animation
return
}
// Fallback to any element type
let anyTab = app.descendants(matching: .any).matching(identifier: identifier).firstMatch
XCTAssertTrue(anyTab.waitForExistence(timeout: 5), "Tab '\(identifier)' not found")
anyTab.tap()
Thread.sleep(forTimeInterval: 0.3)
}
func swipeDown() {
let from = app.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.3))
let to = app.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.7))
from.press(forDuration: 0.1, thenDragTo: to)
}
func swipeUp() {
let from = app.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.7))
let to = app.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.3))
from.press(forDuration: 0.1, thenDragTo: to)
}
}Handling iOS System Dialogs
HealthKit Authorization (iOS 26+)
HealthKit dialog is a scrollable sheet with buttons below the fold:
func handleHealthKitDialog() {
let healthAccessText = app.staticTexts["Health Access"]
if healthAccessText.waitForExistence(timeout: 5) {
// Scroll down to reveal buttons
let from = app.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.8))
let to = app.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.3))
from.press(forDuration: 0.1, thenDragTo: to)
// Tap "Don't Allow"
let dontAllowButton = app.buttons["Don't Allow"]
if dontAllowButton.waitForExistence(timeout: 5) {
dontAllowButton.tap()
} else {
// Fallback with predicate
let button = app.buttons.matching(
NSPredicate(format: "label CONTAINS[c] 'Don\\'t Allow'")
).firstMatch
if button.exists { button.tap() }
}
}
}Location Permission
func handleLocationPermission(allow: Bool) {
addUIInterruptionMonitor(withDescription: "Location") { alert -> Bool in
if allow {
if alert.buttons["Allow While Using App"].exists {
alert.buttons["Allow While Using App"].tap()
return true
}
} else {
if alert.buttons["Don't Allow"].exists {
alert.buttons["Don't Allow"].tap()
return true
}
}
return false
}
}Notification Permission
func handleNotificationPermission(allow: Bool) {
addUIInterruptionMonitor(withDescription: "Notification") { alert -> Bool in
let button = allow ? "Allow" : "Don't Allow"
if alert.buttons[button].exists {
alert.buttons[button].tap()
return true
}
return false
}
}Waiting Patterns
Wait for Network Data to Load
func waitForDataToLoad(timeout: TimeInterval = 30) {
// Wait for loading indicator to disappear
let loadingIndicator = app.activityIndicators.firstMatch
if loadingIndicator.exists {
let gone = waitForNonExistence(loadingIndicator, timeout: timeout)
XCTAssertTrue(gone, "Loading indicator did not disappear")
}
// Wait for content to appear
let content = app.scrollViews["main-content"].firstMatch
XCTAssertTrue(content.waitForExistence(timeout: timeout), "Content did not load")
}
func waitForNonExistence(_ element: XCUIElement, timeout: TimeInterval) -> Bool {
let predicate = NSPredicate(format: "exists == false")
let expectation = XCTNSPredicateExpectation(predicate: predicate, object: element)
let result = XCTWaiter().wait(for: [expectation], timeout: timeout)
return result == .completed
}Wait for Animation to Complete
func waitForAnimation() {
// Simple delay (last resort)
Thread.sleep(forTimeInterval: 0.5)
}
func waitForElementToSettle(_ element: XCUIElement, timeout: TimeInterval = 3) {
var previousFrame = CGRect.zero
let deadline = Date().addingTimeInterval(timeout)
while Date() < deadline {
guard element.exists else {
Thread.sleep(forTimeInterval: 0.1)
continue
}
let currentFrame = element.frame
if currentFrame == previousFrame && !currentFrame.isEmpty {
return // Frame stable
}
previousFrame = currentFrame
Thread.sleep(forTimeInterval: 0.1)
}
}Wait with Retry
func waitAndRetry<T>(
maxAttempts: Int = 3,
delay: TimeInterval = 1,
action: () throws -> T
) rethrows -> T {
var lastError: Error?
for attempt in 1...maxAttempts {
do {
return try action()
} catch {
lastError = error
if attempt < maxAttempts {
Thread.sleep(forTimeInterval: delay)
}
}
}
throw lastError!
}Element Query Patterns
Find by Partial Text
// Contains text (case insensitive)
let errorText = app.staticTexts.matching(
NSPredicate(format: "label CONTAINS[c] 'error'")
).firstMatch
// Starts with
let titleText = app.staticTexts.matching(
NSPredicate(format: "label BEGINSWITH 'Welcome'")
).firstMatch
// Ends with
let buttonText = app.buttons.matching(
NSPredicate(format: "label ENDSWITH 'More'")
).firstMatch
// Regex (use MATCHES)
let versionText = app.staticTexts.matching(
NSPredicate(format: "label MATCHES 'v[0-9]+\\.[0-9]+'")
).firstMatchFind Element in Hierarchy
// Element inside a specific cell
let cell = app.cells["user-cell-0"]
let deleteButton = cell.buttons["Delete"]
// Element inside scroll view
let scrollView = app.scrollViews["main-scroll"]
let card = scrollView.descendants(matching: .any).matching(identifier: "card-1").firstMatch
// Element with parent matching criteria
let submitButton = app.descendants(matching: .button)
.containing(NSPredicate(format: "identifier == 'submit'"))
.firstMatchCheck if Table/List is Empty
func isTableEmpty(_ tableIdentifier: String) -> Bool {
let table = app.tables[tableIdentifier]
return table.cells.count == 0
}
func waitForTableToPopulate(_ tableIdentifier: String, timeout: TimeInterval = 10) {
let predicate = NSPredicate(format: "count > 0")
let cells = app.tables[tableIdentifier].cells
let expectation = XCTNSPredicateExpectation(predicate: predicate, object: cells)
let result = XCTWaiter().wait(for: [expectation], timeout: timeout)
XCTAssertEqual(result, .completed, "Table did not populate")
}Scroll Patterns
Scroll Until Element Visible
func scrollUntilVisible(_ element: XCUIElement, in scrollView: XCUIElement, maxScrolls: Int = 10) {
var scrollCount = 0
while !element.isHittable && scrollCount < maxScrolls {
scrollView.swipeUp()
scrollCount += 1
Thread.sleep(forTimeInterval: 0.3)
}
XCTAssertTrue(element.isHittable, "Element not visible after \(maxScrolls) scrolls")
}Scroll to Top
func scrollToTop(_ scrollView: XCUIElement) {
let statusBar = app.statusBars.firstMatch
if statusBar.exists {
statusBar.tap() // iOS convention: tap status bar scrolls to top
} else {
// Manual scroll
for _ in 0..<10 {
scrollView.swipeDown()
}
}
}Text Field Patterns
Clear and Type
extension XCUIElement {
func clearAndType(_ text: String) {
guard let currentValue = value as? String else {
tap()
typeText(text)
return
}
if currentValue.isEmpty {
tap()
typeText(text)
return
}
// Select all and delete
tap()
press(forDuration: 1.0)
let selectAll = XCUIApplication().menuItems["Select All"]
if selectAll.waitForExistence(timeout: 2) {
selectAll.tap()
typeText(text) // Replaces selection
} else {
// Fallback: delete character by character
let deleteString = String(repeating: XCUIKeyboardKey.delete.rawValue,
count: currentValue.count)
typeText(deleteString + text)
}
}
}Dismiss Keyboard
func dismissKeyboard() {
// Tap outside text field
app.tap()
// Or use keyboard button if available
if app.buttons["Done"].exists {
app.buttons["Done"].tap()
} else if app.buttons["Return"].exists {
app.buttons["Return"].tap()
}
}Debugging
Print Element Hierarchy
func printHierarchy() {
print("=== APP HIERARCHY ===")
print(app.debugDescription)
print("=====================")
}Log Element State
func logElementState(_ element: XCUIElement, name: String) {
print("=== \(name) ===")
print("exists: \(element.exists)")
print("isHittable: \(element.isHittable)")
print("isEnabled: \(element.isEnabled)")
print("label: \(element.label)")
print("value: \(String(describing: element.value))")
print("frame: \(element.frame)")
print("================")
}Diagnostic Test
func testDiagnostic() {
takeScreenshot("00-launch")
print("=== DIAGNOSTIC ===")
print("Buttons: \(app.buttons.count)")
print("StaticTexts: \(app.staticTexts.count)")
print("TextFields: \(app.textFields.count)")
// Check specific elements
let elements = ["tab-home", "submit-button", "main-scroll"]
for id in elements {
let el = app.descendants(matching: .any).matching(identifier: id).firstMatch
print("\(id): \(el.exists ? "FOUND" : "NOT FOUND")")
}
print("==================")
printHierarchy()
takeScreenshot("01-after-diagnostic")
}Test Organization
Test Naming Convention
// Format: test_<feature>_<scenario>_<expectedResult>
func test_login_validCredentials_navigatesToHome() { }
func test_login_invalidPassword_showsError() { }
func test_profile_editName_savesSuccessfully() { }Test with Multiple Assertions
func test_userProfile_displaysAllFields() {
navigateToProfile()
// Soft assertions (continue after failure)
let originalContinueAfterFailure = continueAfterFailure
continueAfterFailure = true
XCTAssertTrue(app.staticTexts["name"].exists, "Name should be visible")
XCTAssertTrue(app.staticTexts["email"].exists, "Email should be visible")
XCTAssertTrue(app.staticTexts["phone"].exists, "Phone should be visible")
XCTAssertTrue(app.images["avatar"].exists, "Avatar should be visible")
continueAfterFailure = originalContinueAfterFailure
}Skip Test Conditionally
func test_featureOnlyOnIPad() throws {
try XCTSkipIf(UIDevice.current.userInterfaceIdiom != .pad,
"This test only runs on iPad")
// iPad-specific test
}
func test_requiresNetwork() throws {
try XCTSkipUnless(isNetworkAvailable(),
"Network required for this test")
// Network-dependent test
}Navigation: XCTest
Test cases and test methods
Customizing Test Setup and Teardown
- Set Up and Tear Down State in Your Tests
- class func setUp())
- func addTeardownBlock(() async throws -> Void)-2guon)
- func addTeardownBlock(() throws -> Void)-5zw6c)
- class func tearDown())
Managing Test Case Execution
- class var runsForEachTargetApplicationUIConfiguration: Bool
- var continueAfterFailure: Bool
- var executionTimeAllowance: TimeInterval
Measuring Performance
- func measure(() -> Void))
- [func measureMetrics([XCTPerformanceMetric], automaticallyStartMeasuring: Bool, for: () -> Void)](/documentation/xctest/xctestcase/measuremetrics(_:automaticallystartmeasuring:for:))
- [func measure(metrics: [any XCTMetric], block: () -> Void)](/documentation/xctest/xctestcase/measure(metrics:block:))
- [func measure(metrics: [any XCTMetric], options: XCTMeasureOptions, block: () -> Void)](/documentation/xctest/xctestcase/measure(metrics:options:block:))
- func measure(options: XCTMeasureOptions, block: () -> Void))
- func startMeasuring())
- func stopMeasuring())
- [class var defaultPerformanceMetrics: [XCTPerformanceMetric]](/documentation/xctest/xctestcase/defaultperformancemetrics)
- [class var defaultMetrics: [any XCTMetric]](/documentation/xctest/xctestcase/defaultmetrics)
- class var defaultMeasureOptions: XCTMeasureOptions
- XCTPerformanceMetric
Measuring Elapsed Time
Initializing an Item
Creating Asynchronous Test Expectations
- func expectation(description: String) -> XCTestExpectation)
- func expectation(for: NSPredicate, evaluatedWith: Any?, handler: XCTNSPredicateExpectation.Handler?) -> XCTestExpectation)
- func expectation(forNotification: NSNotification.Name, object: Any?, handler: XCTNSNotificationExpectation.Handler?) -> XCTestExpectation)
- func expectation(forNotification: NSNotification.Name, object: Any?, notificationCenter: NotificationCenter, handler: XCTNSNotificationExpectation.Handler?) -> XCTestExpectation)
- func keyValueObservingExpectation(for: Any, keyPath: String, expectedValue: Any?) -> XCTestExpectation)
- func expectation<T, V>(that: KeyPath<T, V>, on: T, options: NSKeyValueObservingOptions, willEqual: V) -> XCTKeyPathExpectation<T, V>)
- func keyValueObservingExpectation(for: Any, keyPath: String, handler: XCTKVOExpectation.Handler?) -> XCTestExpectation)
- func expectation<T, V>(that: KeyPath<T, V>, on: T, options: NSKeyValueObservingOptions, willSatisfy: XCTKeyPathExpectation<T, V>.Predicate?) -> XCTKeyPathExpectation<T, V>-6itb)
- func expectation<T, V>(that: KeyPath<T, V>, on: T, options: NSKeyValueObservingOptions, willSatisfy: XCTKeyPathExpectation<T, V>.AsynchronousFilter?) -> XCTKeyPathExpectation<T, V>-292oj)
- func expectation<T, V>(that: KeyPath<T, V>, on: T, options: NSKeyValueObservingOptions, willSatisfy: XCTKeyPathExpectation<T, V>.SynchronousFilter?) -> XCTKeyPathExpectation<T, V>-85or0)
Waiting for Expectations
- [func fulfillment(of: [XCTestExpectation], timeout: TimeInterval, enforceOrder: Bool) async](/documentation/xctest/xctestcase/fulfillment(of:timeout:enforceorder:))
- [func wait(for: [XCTestExpectation])](/documentation/xctest/xctestcase/wait(for:))
- [func wait(for: [XCTestExpectation], enforceOrder: Bool)](/documentation/xctest/xctestcase/wait(for:enforceorder:))
- [func wait(for: [XCTestExpectation], timeout: TimeInterval)](/documentation/xctest/xctestcase/wait(for:timeout:))
- [func wait(for: [XCTestExpectation], timeout: TimeInterval, enforceOrder: Bool)](/documentation/xctest/xctestcase/wait(for:timeout:enforceorder:))
- func waitForExpectations(timeout: TimeInterval, handler: (((any Error)?) -> Void)?))
- XCWaitCompletionHandler
- XCTestError
Classifying Errors with Codes
- static var timeoutWhileWaiting: XCTestError.Code
- static var failureWhileWaiting: XCTestError.Code
- XCTestError.Code
Error Codes
Initializers
Classifying Errors by Domain
Type Properties
Error Codes
Initializers
Monitoring UI Interruptions
- Handling UI Interruptions
- func addUIInterruptionMonitor(withDescription: String, handler: (XCUIElement) -> Bool) -> any NSObjectProtocol)
- func removeUIInterruptionMonitor(any NSObjectProtocol))
Creating Tests Programmatically
- init(invocation: NSInvocation?))
- init(selector: Selector))
- [class var testInvocations: [NSInvocation]](/documentation/xctest/xctestcase/testinvocations)
- var invocation: NSInvocation?
- func invokeTest())
- func record(XCTIssue))
- func recordFailure(withDescription: String, inFile: String, atLine: Int, expected: Bool))
- class var defaultTestSuite: XCTestSuite
Examining Test Properties
Setting Up and Tearing Down
- func setUp(completion: ((any Error)?) -> Void))
- func setUpWithError() throws)
- func setUp())
- func tearDown(completion: ((any Error)?) -> Void))
- func tearDownWithError() throws)
- func tearDown())
Running Tests
Test assertions
Tests for True Conditions
- func XCTAssert(@autoclosure () throws -> Bool, @autoclosure () -> String, file: StaticString, line: UInt))
- func XCTAssertTrue(@autoclosure () throws -> Bool, @autoclosure () -> String, file: StaticString, line: UInt))
Tests for False Conditions
Tests for a Nil Condition
Tests for a Non-Nil Condition
- func XCTAssertNotNil(@autoclosure () throws -> Any?, @autoclosure () -> String, file: StaticString, line: UInt))
- func XCTUnwrap<T>(@autoclosure () throws -> T?, @autoclosure () -> String, file: StaticString, line: UInt) throws -> T)
Tests for Equality and Inequality
- func XCTAssertEqual<T>(@autoclosure () throws -> T, @autoclosure () throws -> T, @autoclosure () -> String, file: StaticString, line: UInt))
- func XCTAssertNotEqual<T>(@autoclosure () throws -> T, @autoclosure () throws -> T, @autoclosure () -> String, file: StaticString, line: UInt))
Tests for Identical Objects
- func XCTAssertIdentical(@autoclosure () throws -> AnyObject?, @autoclosure () throws -> AnyObject?, @autoclosure () -> String, file: StaticString, line: UInt))
- func XCTAssertNotIdentical(@autoclosure () throws -> AnyObject?, @autoclosure () throws -> AnyObject?, @autoclosure () -> String, file: StaticString, line: UInt))
Tests for Equality Within a Specified Accuracy
- func XCTAssertEqual<T>(@autoclosure () throws -> T, @autoclosure () throws -> T, accuracy: T, @autoclosure () -> String, file: StaticString, line: UInt)-6frfw)
- func XCTAssertEqual<T>(@autoclosure () throws -> T, @autoclosure () throws -> T, accuracy: T, @autoclosure () -> String, file: StaticString, line: UInt)-4epu5)
- func XCTAssertNotEqual<T>(@autoclosure () throws -> T, @autoclosure () throws -> T, accuracy: T, @autoclosure () -> String, file: StaticString, line: UInt)-7jcd6)
- func XCTAssertNotEqual<T>(@autoclosure () throws -> T, @autoclosure () throws -> T, accuracy: T, @autoclosure () -> String, file: StaticString, line: UInt)-326vc)
Tests for Comparable Values
- func XCTAssertGreaterThan<T>(@autoclosure () throws -> T, @autoclosure () throws -> T, @autoclosure () -> String, file: StaticString, line: UInt))
- func XCTAssertGreaterThanOrEqual<T>(@autoclosure () throws -> T, @autoclosure () throws -> T, @autoclosure () -> String, file: StaticString, line: UInt))
- func XCTAssertLessThanOrEqual<T>(@autoclosure () throws -> T, @autoclosure () throws -> T, @autoclosure () -> String, file: StaticString, line: UInt))
- func XCTAssertLessThan<T>(@autoclosure () throws -> T, @autoclosure () throws -> T, @autoclosure () -> String, file: StaticString, line: UInt))
Tests for Errors
Unconditional Test Failures
Expected Failures
Detailing Expected Failure
Setting Options
Matching Failures
Specifying Options
Matching Failures
Specifying Options
- func XCTExpectFailure(String?, options: XCTExpectedFailure.Options))
- func XCTExpectFailure(String?, enabled: Bool?, strict: Bool?, issueMatcher: ((XCTIssue) -> Bool)?))
- func XCTExpectFailure<R>(String?, options: XCTExpectedFailure.Options, failingBlock: () throws -> R) rethrows -> R)
- func XCTExpectFailure<R>(String?, enabled: Bool?, strict: Bool?, failingBlock: () throws -> R, issueMatcher: ((XCTIssue) -> Bool)?) rethrows -> R)
Methods for Skipping Tests
- func XCTSkipIf(@autoclosure () throws -> Bool, @autoclosure () -> String?, file: StaticString, line: UInt) throws)
- func XCTSkipUnless(@autoclosure () throws -> Bool, @autoclosure () -> String?, file: StaticString, line: UInt) throws)
- XCTSkip
Skipping a Test
Describing a Skipped Test
Asynchronous tests
Expectations
Creating Expectations
Fulfilling Expectations
Fulfillment Count
Unintended Expectations
Key Value Observing Expectations
Creating KVO expectations
- convenience init(keyPath: String, object: Any))
- convenience init(keyPath: String, object: Any, expectedValue: Any?))
- init(keyPath: String, object: Any, expectedValue: Any?, options: NSKeyValueObservingOptions))
Expectation properties
- var keyPath: String
- var observedObject: Any
- var expectedValue: Any?
- var options: NSKeyValueObservingOptions
Custom KVO evaluation
Creating key path expectations
- convenience init(keyPath: KeyPath<T, V>, observedObject: T, options: NSKeyValueObservingOptions, expectedValue: V))
- convenience init(keyPath: KeyPath<T, V>, observedObject: T, options: NSKeyValueObservingOptions, predicate: XCTKeyPathExpectation<T, V>.Predicate?))
- convenience init(keyPath: KeyPath<T, V>, observedObject: T, options: NSKeyValueObservingOptions, filter: XCTKeyPathExpectation<T, V>.SynchronousFilter?)-plka)
- convenience init(keyPath: KeyPath<T, V>, observedObject: T, options: NSKeyValueObservingOptions, filter: XCTKeyPathExpectation<T, V>.AsynchronousFilter?)-8noag)
- XCTKeyPathExpectation.AsynchronousFilter
- XCTKeyPathExpectation.SynchronousFilter
- XCTKeyPathExpectation.Predicate
Expectation properties
- let keyPath: KeyPath<T, V>
- let observedObject: T
- let options: NSKeyValueObservingOptions
- var expectedValue: V?
Notification-Based Expectations
Creating NSNotification Expectations
- convenience init(name: NSNotification.Name))
- convenience init(name: NSNotification.Name, object: Any?))
- init(name: NSNotification.Name, object: Any?, notificationCenter: NotificationCenter))
Expectation Properties
- var notificationName: NSNotification.Name
- var observedObject: Any?
- var notificationCenter: NotificationCenter
Custom Notification Evaluation
Creating Darwin Notification Expectations
Expectation Properties
Custom Notification Evaluation
Predicate-Based Expectations
Creating a Predicate-Based Expectation
Expectation Properties
Handling Predicate Resolution
Expectation Waiters
Creating a Waiter
Waiting for Expectations
- [func fulfillment(of: [XCTestExpectation], timeout: TimeInterval, enforceOrder: Bool) async -> XCTWaiter.Result](/documentation/xctest/xctwaiter/fulfillment(of:timeout:enforceorder:)-swift.method)
- [func wait(for: [XCTestExpectation]) -> XCTWaiter.Result](/documentation/xctest/xctwaiter/wait(for:)-swift.method)
- [func wait(for: [XCTestExpectation], enforceOrder: Bool) -> XCTWaiter.Result](/documentation/xctest/xctwaiter/wait(for:enforceorder:)-swift.method)
- [func wait(for: [XCTestExpectation], timeout: TimeInterval) -> XCTWaiter.Result](/documentation/xctest/xctwaiter/wait(for:timeout:)-swift.method)
- [func wait(for: [XCTestExpectation], timeout: TimeInterval, enforceOrder: Bool) -> XCTWaiter.Result](/documentation/xctest/xctwaiter/wait(for:timeout:enforceorder:)-swift.method)
- [class func fulfillment(of: [XCTestExpectation], timeout: TimeInterval, enforceOrder: Bool) async -> XCTWaiter.Result](/documentation/xctest/xctwaiter/fulfillment(of:timeout:enforceorder:)-swift.type.method)
- [class func wait(for: [XCTestExpectation]) -> XCTWaiter.Result](/documentation/xctest/xctwaiter/wait(for:)-swift.type.method)
- [class func wait(for: [XCTestExpectation], enforceOrder: Bool) -> XCTWaiter.Result](/documentation/xctest/xctwaiter/wait(for:enforceorder:)-swift.type.method)
- [class func wait(for: [XCTestExpectation], timeout: TimeInterval) -> XCTWaiter.Result](/documentation/xctest/xctwaiter/wait(for:timeout:)-swift.type.method)
- [class func wait(for: [XCTestExpectation], timeout: TimeInterval, enforceOrder: Bool) -> XCTWaiter.Result](/documentation/xctest/xctwaiter/wait(for:timeout:enforceorder:)-swift.type.method)
- XCTWaiter.Result
Result States
Initializers
Responding to Expectation Fulfilment
Timeout Events
- [func waiter(XCTWaiter, didTimeoutWithUnfulfilledExpectations: [XCTestExpectation])](/documentation/xctest/xctwaiterdelegate/waiter(_:didtimeoutwithunfulfilledexpectations:))
- func nestedWaiter(XCTWaiter, wasInterruptedByTimedOutWaiter: XCTWaiter))
Order of Fulfillment Events
Inverted Expectation Events
- [var fulfilledExpectations: [XCTestExpectation]](/documentation/xctest/xctwaiter/fulfilledexpectations)
UI tests
Performance tests
Measuring Performance
Measurement Options
Getting Default Options
Using Option Details
Measurement Options
- static var manuallyStart: XCTMeasureOptions.InvocationOptions
- static var manuallyStop: XCTMeasureOptions.InvocationOptions
Initializers
Measurement Metrics
Recording Metrics
Reporting Gathered Metrics
- [func reportMeasurements(from: XCTPerformanceMeasurementTimestamp, to: XCTPerformanceMeasurementTimestamp) throws -> [XCTPerformanceMeasurement]](/documentation/xctest/xctmetric/reportmeasurements(from:to:))
Initializers
Initializing an Item
Creating a hitch metric
Initializers
Measuring Specific Signposts
Measuring Navigation Transitions
- class var customNavigationTransitionMetric: any XCTMetric
- class var navigationTransitionMetric: any XCTMetric
Measuring Scrolling Properties
Deprecated
- class var applicationLaunch: XCTOSSignpostMetric
- class var scrollDecelerationMetric: any XCTMetric
- class var scrollDraggingMetric: any XCTMetric
Initializers
Initializers
Measurements
Initializing a Measurement
- init(identifier: String, displayName: String, doubleValue: Double, unitSymbol: String))
- convenience init(identifier: String, displayName: String, doubleValue: Double, unitSymbol: String, polarity: XCTPerformanceMeasurement.Polarity))
- convenience init(identifier: String, displayName: String, value: Measurement<Unit>))
- convenience init(identifier: String, displayName: String, value: Measurement<Unit>, polarity: XCTPerformanceMeasurement.Polarity))
Identifying Measurements
Accessing Measured Values
- var doubleValue: Double
- var unitSymbol: String
- var value: Measurement<Unit>
- var polarity: XCTPerformanceMeasurement.Polarity
- XCTPerformanceMeasurement.Polarity
Polarity Types
Initializers
Initializers
Mach Absolute Time
Date
Activities and attachments
Activities
Running Activities
Adding Attachments
Activity Properties
Attachments
Creating Attachments from Data
- convenience init(data: Data))
- convenience init(data: Data, uniformTypeIdentifier: String))
- [init(uniformTypeIdentifier: String?, name: String?, payload: Data?, userInfo: [AnyHashable : Any]?)](/documentation/xctest/xctattachment/init(uniformtypeidentifier:name:payload:userinfo:))
Creating Attachments from Files and Folders
- convenience init(contentsOfFileAtURL: URL))
- convenience init(contentsOfFileAtURL: URL, uniformTypeIdentifier: String))
- convenience init(compressedContentsOfDirectoryAtURL: URL))
Creating Attachments from Images and Screenshots
- convenience init(image: UIImage))
- convenience init(image: UIImage, quality: XCTAttachment.ImageQuality))
- convenience init(screenshot: XCUIScreenshot))
- convenience init(screenshot: XCUIScreenshot, quality: XCTAttachment.ImageQuality))
- XCUIScreenshot
- XCTAttachment.ImageQuality
Quality Settings
Initializers
Creating Attachments from Objects
- convenience init(plistObject: Any))
- convenience init(archivableObject: any NSSecureCoding))
- convenience init(archivableObject: any NSSecureCoding, uniformTypeIdentifier: String))
Creating Attachments from Strings
Setting an Attachment’s Lifetime
Attachment Lifetimes
Initializers
Attachment Metadata
- var name: String?
- var uniformTypeIdentifier: String
- [var userInfo: [AnyHashable : Any]?](/documentation/xctest/xctattachment/userinfo)
Initializers
- convenience init(compressedContentsOfDirectory: URL))
- convenience init(contentsOfFile: URL))
- convenience init(contentsOfFile: URL, uniformTypeIdentifier: String))
Default Implementations
Initializers
- convenience init(compressedContentsOfDirectoryAtURL: URL))
- convenience init(contentsOfFileAtURL: URL))
- convenience init(contentsOfFileAtURL: URL, uniformTypeIdentifier: String))
Test execution
Test Failures
Issue Types
Issue Details
- var type: XCTIssue.IssueType
- var compactDescription: String
- var detailedDescription: String?
- var sourceCodeContext: XCTSourceCodeContext
- var associatedError: (any Error)?
- [var attachments: [XCTAttachment]](/documentation/xctest/xctissue-swift.struct/attachments)
- func add(XCTAttachment))
Initializers
- [init(type: XCTIssue.IssueType, compactDescription: String, detailedDescription: String?, sourceCodeContext: XCTSourceCodeContext, associatedError: (any Error)?, attachments: [XCTAttachment], severity: XCTIssue.Severity)](/documentation/xctest/xctissue-swift.struct/init(type:compactdescription:detaileddescription:sourcecodecontext:associatederror:attachments:severity:))
Instance Properties
Type Aliases
Initializers
- convenience init(type: XCTIssueReference.IssueType, compactDescription: String))
- [init(type: XCTIssueReference.IssueType, compactDescription: String, detailedDescription: String?, sourceCodeContext: XCTSourceCodeContext, associatedError: (any Error)?, attachments: [XCTAttachment])](/documentation/xctest/xctissuereference/init(type:compactdescription:detaileddescription:sourcecodecontext:associatederror:attachments:))
- [init(type: XCTIssueReference.IssueType, compactDescription: String, detailedDescription: String?, sourceCodeContext: XCTSourceCodeContext, associatedError: (any Error)?, attachments: [XCTAttachment], severity: XCTIssueReference.Severity)](/documentation/xctest/xctissuereference/init(type:compactdescription:detaileddescription:sourcecodecontext:associatederror:attachments:severity:))
- convenience init(type: XCTIssueReference.IssueType, compactDescription: String, severity: XCTIssueReference.Severity))
Issue Types
Issue Types
- case assertionFailure
- case performanceRegression
- case system
- case thrownError
- case uncaughtException
- case unmatchedExpectedFailure
Initializers
Issue Details
- var type: XCTIssueReference.IssueType
- var compactDescription: String
- var detailedDescription: String?
- var sourceCodeContext: XCTSourceCodeContext
- var associatedError: (any Error)?
- [var attachments: [XCTAttachment]](/documentation/xctest/xctissuereference/attachments)
Instance Properties
Enumerations
Enumeration Cases
Initializers
Issue Details
- var type: XCTIssueReference.IssueType
- var compactDescription: String
- var detailedDescription: String?
- var sourceCodeContext: XCTSourceCodeContext
- var associatedError: (any Error)?
- [var attachments: [XCTAttachment]](/documentation/xctest/xctmutableissue/attachments)
- func add(XCTAttachment))
Instance Properties
Initializers
- [init(callStack: [XCTSourceCodeFrame], location: XCTSourceCodeLocation?)](/documentation/xctest/xctsourcecodecontext/init(callstack:location:))
- [convenience init(callStackAddresses: [NSNumber], location: XCTSourceCodeLocation?)](/documentation/xctest/xctsourcecodecontext/init(callstackaddresses:location:))
- convenience init(location: XCTSourceCodeLocation?))
- convenience init())
Context Information
- [var callStack: [XCTSourceCodeFrame]](/documentation/xctest/xctsourcecodecontext/callstack)
- var location: XCTSourceCodeLocation?
Initializers
Frame Information
- var address: UInt64
- var symbolInfo: XCTSourceCodeSymbolInfo?
- var symbolicationError: (any Error)?
- func symbolInfo() throws -> XCTSourceCodeSymbolInfo)
Initializers
- init(fileURL: URL, lineNumber: Int))
- convenience init(filePath: String, lineNumber: Int)-3hzmr)
- convenience init(filePath: StaticString, lineNumber: UInt)-8qw52)
Source Location Information
Initializers
Symbol Information
Test Runs
Deprecated
Managing Test Runs
- func addTestRun(XCTestRun))
- [var testRuns: [XCTestRun]](/documentation/xctest/xctestsuiterun/testruns)
Creating Test Runs
Performing Test Runs
Tracking Test Durations
- var startDate: Date?
- var stopDate: Date?
- var testDuration: TimeInterval
- var totalDuration: TimeInterval
Gathering Test Outcomes
- var hasSucceeded: Bool
- var hasBeenSkipped: Bool
- var executionCount: Int
- var failureCount: Int
- var skipCount: Int
- var test: XCTest
- var testCaseCount: Int
- var totalFailureCount: Int
- var unexpectedExceptionCount: Int
Deprecated
Test Observation
Observation Methods
- func testBundleWillStart(Bundle))
- func testSuiteWillStart(XCTestSuite))
- func testCaseWillStart(XCTestCase))
- func testCase(XCTestCase, didRecord: XCTIssue)-4cou6)
- func testCase(XCTestCase, didRecord: XCTExpectedFailure)-8k955)
- func testCase(XCTestCase, didFailWithDescription: String, inFile: String?, atLine: Int))
- func testCaseDidFinish(XCTestCase))
- func testSuite(XCTestSuite, didRecord: XCTIssue)-3rk9k)
- func testSuite(XCTestSuite, didRecord: XCTExpectedFailure)-1xjkv)
- func testSuite(XCTestSuite, didFailWithDescription: String, inFile: String?, atLine: Int))
- func testSuiteDidFinish(XCTestSuite))
- func testBundleDidFinish(Bundle))
Accessing the Shared Observation Center
Managing Observers
Test Suites
Creating Test Suites
- class var `default`: XCTestSuite
- init(name: String))
- convenience init(forBundlePath: String))
- convenience init(forTestCaseClass: AnyClass))
- convenience init(forTestCaseWithName: String))
Managing Tests
- func addTest(XCTest))
- [var tests: [XCTest]](/documentation/xctest/xctestsuite/tests)
Deprecated
Deprecated Classes
Logging Test Results
Starting and Stopping Test Observation
Monitoring Test Activity
- func testCaseDidFail(XCTestRun!, withDescription: String!, inFile: String!, atLine: Int))
- func testCaseDidStart(XCTestRun!))
- func testCaseDidStop(XCTestRun!))
- func testSuiteDidStart(XCTestRun!))
- func testSuiteDidStop(XCTestRun!))
Inspecting a Test
Deprecated Functions
- func XCTSelfTestMain() -> Int32)
- func XCTAssertEqualWithAccuracy<T>(@autoclosure () throws -> T, @autoclosure () throws -> T, accuracy: T, @autoclosure () -> String, file: StaticString, line: UInt))
- func XCTAssertNotEqualWithAccuracy<T>(@autoclosure () throws -> T, @autoclosure () throws -> T, T, @autoclosure () -> String, file: StaticString, line: UInt))
Deprecated Constants
- let XCTestObserverClassKey: String
- let XCTestedUnitPath: String
- let XCTestScopeNone: String
- let XCTestScopeAll: String
- let XCTestScopeKey: String
- let XCTestScopeSelf: String
- let XCTestToolKey: String
Variables
Functions
---
Extracted from Apple DocC JSON by apple-skills tooling. This is unofficial content. All documentation belongs to Apple Inc.
Navigation: XCUIAutomation
Class
XCUIApplication
Available on: iOS, iPadOS, Mac Catalyst, macOS, tvOS, visionOS, watchOS, Xcode 16.3+
A proxy that can launch, monitor, and terminate a test application.
@MainActor class XCUIApplicationOverview
Use this class to launch, monitor, and terminate your app in a UI test. Use wait(for:timeout:)) to launch your app and wait for it to reach an expected state before you check test conditions.
Inherits From
Conforms To
- CVarArg
- CustomDebugStringConvertible
- CustomStringConvertible
- Equatable
- Hashable
- NSObjectProtocol
- XCUIElementAttributes
- XCUIElementSnapshotProviding
- XCUIElementTypeQueryProvider
- XCUIScreenshotProviding
Creating an application proxy
- init()) Creates a proxy for the application that’s configured as the Target Application in Xcode’s target settings.
- init(bundleIdentifier:)) Creates a proxy for an application for the specified bundle identifier.
- init(url:)-90e7z) Creates a proxy for the application at the specified file system URL.
Launching the application
- launch()) Launches the application.
- launchArguments The arguments that pass to the application on launch.
- launchEnvironment The environment variables that pass to the application on launch.
- open(_:)) Launches the application by URL.
Activating the application
- activate()) Activates the application.
Terminating the application
- terminate()) Terminates any running instance of the application.
Determining application state
- state The most recent state of the application.
- XCUIApplication.State The possible states of an application during UI testing.
Waiting for an application state
- wait(for:timeout:)) Waits for the application to reach the specified state or timeout.
Resetting authorization status
- resetAuthorizationStatus(for:)) Resets the authorization status for a protected resource.
- XCUIProtectedResource A system resource that requires user authorization to access.
Performing an accessibility audit
Initializers
- init(URL:)-6ga10)
---
Extracted from Apple DocC JSON by apple-skills tooling. This is unofficial content. All documentation belongs to Apple Inc.
Navigation: XCUIAutomation
Class
XCUICoordinate
Available on: iOS, iPadOS, Mac Catalyst, macOS, visionOS, watchOS, Xcode 16.3+
A location on screen relative to a UI element.
@MainActor class XCUICoordinateOverview
Coordinates are dynamic, like the elements to which they refer, and may compute different screen locations at different times, or be invalid if the element they reference doesn’t exist.
Inherits From
Conforms To
- CVarArg
- CustomDebugStringConvertible
- CustomStringConvertible
- Equatable
- Hashable
- NSCopying
- NSObjectProtocol
- Sendable
Getting coordinate properties
- referencedElement The element that the coordinate is based on, either directly or through the coordinate from which it was derived.
- screenPoint The dynamically computed value of the coordinate’s location on screen.
Moving the pointer
- hover()) Moves the pointer to the coordinate.
Clicking
- click()) Sends a click event at the coordinate.
- click(forDuration:thenDragTo:)) Clicks and holds for a duration you specify, then drags to the other coordinate.
- click(forDuration:thenDragTo:withVelocity:thenHoldForDuration:)) Clicks and holds for a duration, drags at a velocity, and holds over the other coordinate for a duration, all of which you specify.
- doubleClick()) Sends a double-click event at the coordinate.
- rightClick()) Sends a Control-click event at the coordinate.
Scrolling
- scroll(byDeltaX:deltaY:)) Scrolls the view by the number of x and y pixels you specify.
Tapping and pressing
- tap()) Sends a tap event at the coordinate.
- doubleTap()) Sends a double-tap event at the coordinate.
- press(forDuration:)) Initiates a press-and-hold gesture at the coordinate, holding for the duration you specify.
- press(forDuration:thenDragTo:)) Initiates a press-and-hold gesture at the coordinate, then drags to another coordinate.
- press(forDuration:thenDragTo:withVelocity:thenHoldForDuration:)) Initiates a press-and-hold gesture, drags to another coordinate with a velocity you specify, and holds for a duration you specify.
Performing gestures
- swipeLeft()) Sends a swipe-left gesture.
- swipeLeft(velocity:)) Sends a swipe-left gesture with a velocity you specify.
- swipeRight()) Sends a swipe-right gesture.
- swipeRight(velocity:)) Sends a swipe-right gesture with a velocity you specify.
- swipeUp()) Sends a swipe-up gesture.
- swipeUp(velocity:)) Sends a swipe-up gesture with a velocity you specify.
- swipeDown()) Sends a swipe-down gesture.
- swipeDown(velocity:)) Sends a swipe-down gesture with a velocity you specify.
Creating relative coordinates
- withOffset(_:)) Creates a new coordinate with an absolute offset in points from the original coordinate.
UI elements
- XCUIElement A UI element in an application.
- XCUIElementAttributes Attributes exposed by UI elements.
- XCUIElementSnapshot A set of attributes to express a snapshot of an element’s attributes and descendant user interface hierarchy.
- XCUIElementSnapshotProviding A method to capture a snapshot of an element’s attributes and descendant user interface hierarchy.
---
Extracted from Apple DocC JSON by apple-skills tooling. This is unofficial content. All documentation belongs to Apple Inc.
Navigation: XCUIAutomation
Class
XCUIElement
Available on: iOS, iPadOS, Mac Catalyst, macOS, tvOS, visionOS, watchOS, Xcode 16.3+
A UI element in an application.
@MainActor class XCUIElementOverview
In macOS and iPadOS 15 and later, XCUIElement provides a way to test your app with keyboard and mouse interactions, such as typing, clicking, scrolling, and moving and pausing the pointer. In iOS, XCUIElement provides a way to test your app with gestures, such as tapping, swiping, pinching, and rotating.
Note: XCUIElement adopts the XCUIElementAttributes protocol, which provides additional properties for querying the current state of a UI element’s attributes.
Inherits From
Inherited By
Conforms To
- CVarArg
- Copyable
- CustomDebugStringConvertible
- CustomStringConvertible
- Equatable
- Escapable
- Hashable
- NSObjectProtocol
- Sendable
- XCUIElementAttributes
- XCUIElementSnapshotProviding
- XCUIElementTypeQueryProvider
- XCUIScreenshotProviding
Querying element state
- waitForExistence(timeout:)) Waits the specified amount of time for an element to exist.
- waitForNonExistence(timeout:)) Waits the specified amount of time for an element to no longer exist.
- wait(for:toEqual:timeout:)) Waits a specified amount of time for a property value to equal a specified value.
- exists Determines if the element exists.
- isHittable Determines if the system can compute a hit point for the element.
- debugDescription Provides debugging information about the element.
Querying descendant elements
- children(matching:)) Returns a query for all direct children of the element matching the type you specify.
- descendants(matching:)) Returns a query for all descendants of the element matching the type you specify.
Typing text
- typeText(_:)) Types a string into the element.
Combining keystrokes
- typeKey(_:modifierFlags:)-6gaoi) Types a single key from the XCUIKeyboardKey enumeration with the specified modifier flags.
- typeKey(_:modifierFlags:)-9ubn) Types a single key that a string represents with the flags you specify.
- XCUIKeyboardKey Constants to represent keys that have no typewritten equivalent.
- perform(withKeyModifiers:block:)) Executes a block of code while holding a combination keystroke.
- XCUIElement.KeyModifierFlags Flags for simulating combination keystrokes with keys, such as Control, Option, Shift, and Command.
Moving the pointer
- hover()) Moves the pointer over the element.
Clicking
- click()) Sends a click event to a hittable point computed for the element.
- click(forDuration:thenDragTo:)) Clicks and holds an element for a duration you specify, and then drags it to another element.
- click(forDuration:thenDragTo:withVelocity:thenHoldForDuration:)) Clicks and holds an element for a duration, drags it at a velocity, and holds it over another element for a duration, all of which you specify.
- doubleClick()) Sends a double-click event to a hittable point the system computes for the element.
- rightClick()) Sends a Control-click event to a hittable point the system computes for the element.
Scrolling
- scroll(byDeltaX:deltaY:)) Scrolls the view by the number of x and y pixels you specify.
Tapping and pressing
- tap()) Sends a tap event to a hittable point the system computes for the element.
- doubleTap()) Sends a double-tap event to a hittable point the system computes for the element.
- press(forDuration:)) Sends a press-and-hold gesture to a hittable point the system computes for the element, holding for the duration you specify.
- press(forDuration:thenDragTo:)) Initiates a press-and-hold gesture, then drags to another element.
- press(forDuration:thenDragTo:withVelocity:thenHoldForDuration:)) Initiates a press-and-hold gesture, drags to another element at a velocity, and holds for a duration, all of which you specify.
Tapping multiple times
- twoFingerTap()) Sends a two-finger tap event to a hittable point the system computes for the element.
- tap(withNumberOfTaps:numberOfTouches:)) Sends one or more taps with one or more touch points.
Performing gestures
- swipeLeft()) Sends a swipe-left gesture.
- swipeLeft(velocity:)) Sends a swipe-left gesture with a velocity you specify.
- swipeRight()) Sends a swipe-right gesture.
- swipeRight(velocity:)) Sends a swipe-right gesture with a velocity you specify.
- swipeUp()) Sends a swipe-up gesture.
- swipeUp(velocity:)) Sends a swipe-up gesture with a velocity you specify.
- swipeDown()) Sends a swipe-down gesture.
- swipeDown(velocity:)) Sends a swipe-down gesture with a velocity you specify.
- pinch(withScale:velocity:)) Sends a pinching gesture with two touches.
- rotate(_:withVelocity:)) Sends a rotation gesture with two touches.
- XCUIGestureVelocity A value that describes how fast a gesture moves across the screen, in pixels per second.
Interacting with sliders
- normalizedSliderPosition Returns the position of the slider’s indicator as a normalized value.
- adjust(toNormalizedSliderPosition:)) Manipulates the UI to change the value the slider displays to a new value, based on a normalized position.
Interacting with pickers
- adjust(toPickerWheelValue:)) Changes the value that the picker wheel displays.
Calculating coordinates
- coordinate(withNormalizedOffset:)) Creates and returns a new coordinate with a normalized offset.
Supporting types
- XCUIElement.ElementType The types of UI elements that you find, inspect, and interact with in a UI test.
- XCUIElement.SizeClass The user interface size classes you can inspect in a UI test.
- XCUIElement.AttributeName A set of string constants that serve as keys for storing element attributes in a dictionary.
Deprecated methods
- swipeDown(withVelocity:)) Sends a swipe-down gesture with a velocity you specify.
- swipeUp(withVelocity:)) Sends a swipe-up gesture with a velocity you specify.
- swipeLeft(withVelocity:)) Sends a swipe-left gesture with a velocity you specify.
- swipeRight(withVelocity:)) Sends a swipe-right gesture with a velocity you specify.
UI elements
- XCUIElementAttributes Attributes exposed by UI elements.
- XCUIElementSnapshot A set of attributes to express a snapshot of an element’s attributes and descendant user interface hierarchy.
- XCUIElementSnapshotProviding A method to capture a snapshot of an element’s attributes and descendant user interface hierarchy.
- XCUICoordinate A location on screen relative to a UI element.
---
Extracted from Apple DocC JSON by apple-skills tooling. This is unofficial content. All documentation belongs to Apple Inc.
Navigation: XCUIAutomation
Class
XCUIElementQuery
Available on: iOS, iPadOS, Mac Catalyst, macOS, tvOS, visionOS, watchOS, Xcode 16.3+
An object that defines the search criteria a test uses to identify UI elements.
@MainActor class XCUIElementQueryDiscussion
Use element queries to find UI elements in your app that you interact with in the tests, to test for the presence of expected elements, or to discover elements to test their values.
For example, this test uses an element query to find the “Add Book” button, and after clicking the button, checks that there’s one button in an outline view cell titled “Untitled Book”. If the test can’t find the “Add Book” button, or there isn’t one “Untitled Book” cell, then the test fails.
@MainActor
func testClickingAddCreatesAnUntitledBook() throws {
let app = XCUIApplication()
app.launch()
let list = app.windows["Reading Journal"]
list.toolbars.children(matching: .button)["Add Book"].click()
XCTAssertEqual(list.outlines["Sidebar"].cells.containing(.button, identifier:"Untitled Book").count, 1)
}Inherits From
Conforms To
- CVarArg
- CustomDebugStringConvertible
- CustomStringConvertible
- Equatable
- Hashable
- NSObjectProtocol
- Sendable
- XCUIElementTypeQueryProvider
Creating new queries
- children(matching:)) Returns a new query that matches all direct children of the requested type.
- descendants(matching:)) Returns a new query that matches all descendants of the requested type.
- containing(_:)) Returns a new query that matches elements containing a descendant that meets the logical conditions of the provided predicate.
- containing(_:identifier:)) Returns a new query that matches elements that contain a descendant of the requested type and an identifying property that matches a provided identifier.
- matching(identifier:)) Returns a new query that matches elements that have an identifying property that matches a provided identifier.
- matching(_:)) Returns a new query that matches elements that meet the logical conditions of the provided predicate.
- matching(_:identifier:)) Returns a new query that matches elements of the requested type and have an identifying property that matches a provided identifier.
Accessing matched elements
- allElementsBoundByAccessibilityElement Immediately evaluates the query and returns an array of elements bound to the resulting accessibility elements.
- allElementsBoundByIndex Immediately evaluates the query and returns an array of elements bound by the index of each result.
- count Evaluates the query and returns the number of elements that match.
- element The query’s single matching element.
- element(boundBy:)) Uses an index into the query’s results to determine which underlying accessibility element to use.
- element(matching:)) Matches the predicate.
- element(matching:identifier:)) Matches the provided element type and identifier.
- subscript(_:)) Returns a descendant element that matches a provided identifier.
- element(at:)) Returns an element that resolves to the index into the query’s result set.
Debugging element queries
- debugDescription Provides debugging information about the query.
Identifying window buttons
- XCUIIdentifierCloseWindow The identifier for a window’s close button.
- XCUIIdentifierFullScreenWindow The identifier for a window’s full-screen button.
- XCUIIdentifierMinimizeWindow The identifier for a window’s minimize button.
- XCUIIdentifierZoomWindow The identifier for a window’s zoom button.
UI element queries
- XCUIElementTypeQueryProvider A type that provides ready-made queries for locating descendant UI elements.
---
Extracted from Apple DocC JSON by apple-skills tooling. This is unofficial content. All documentation belongs to Apple Inc.
Navigation: XCUIAutomation
Enumeration
XCUIProtectedResource
Available on: iOS 13.4+, iPadOS 13.4+, Mac Catalyst 13.4+, macOS 10.15.4+, tvOS 13.4+, visionOS 1.0+, watchOS 6.2+, Xcode 16.3+
A system resource that requires user authorization to access.
enum XCUIProtectedResourceConforms To
Protected resources
- XCUIProtectedResource.location The protected resource case for Location Services.
- XCUIProtectedResource.userTracking The protected resource case for access to tracking data.
- XCUIProtectedResource.contacts The protected resource case for access to Contacts.
- XCUIProtectedResource.calendar The protected resource case for acces to Calendar data.
- XCUIProtectedResource.reminders The protected resource case for access to Reminders data.
- XCUIProtectedResource.photos The protected resource case for access to Photos.
- XCUIProtectedResource.bluetooth The protected resource case for Bluetooth utilization.
- XCUIProtectedResource.localNetwork The protected resource case for finding and communicating with devices on the local network.
- XCUIProtectedResource.microphone The protected resource case for access to the microphone.
- XCUIProtectedResource.camera The protected resource case for access to the camera.
- XCUIProtectedResource.health The protected resource case for access to Health data.
- XCUIProtectedResource.homeKit The protected resource case for access to Home data.
- XCUIProtectedResource.mediaLibrary The protected resource case for access to the media library.
- XCUIProtectedResource.keyboardNetwork The protected resource case for access to the keyboard network.
- XCUIProtectedResource.systemRootDirectory The protected resource case for access to the system root directory.
- XCUIProtectedResource.userDesktopDirectory The protected resource case for access to the Desktop directory.
- XCUIProtectedResource.userDocumentsDirectory The protected resource case for access to the Documents directory.
- XCUIProtectedResource.userDownloadsDirectory The protected resource case for access to the Downloads directory.
- XCUIProtectedResource.focus The protected resource case to see and share Focus status.
- XCUIProtectedResource.removableVolumes The protected resource case for access to removable volumes.
- XCUIProtectedResource.networkVolumes The protected resource case for access to network volumes.
- XCUIProtectedResource.appleEvents The protected resource case for the use of Apple Events.
Initializers
Resetting authorization status
- resetAuthorizationStatus(for:)) Resets the authorization status for a protected resource.
---
Extracted from Apple DocC JSON by apple-skills tooling. This is unofficial content. All documentation belongs to Apple Inc.
Related skills
How it compares
Pick xcuitest for native iOS XCUITest API reference; use Appium or Detox skills for cross-platform mobile UI automation.
FAQ
What Swift concurrency pattern does xcuitest recommend?
xcuitest documents Swift 6 @MainActor on XCTestCase subclasses because UI interactions must run on the main actor. Test setup initializes XCUIApplication, sets continueAfterFailure to false, and calls app.launch() in setUp.
How does xcuitest handle flaky element waits?
xcuitest covers waitForExistence for simple timeouts, XCTWaiter with NSPredicateExpectation for property changes, and Xcode 16+ waitForNonExistence for loading indicators. KeyPath-based waiting appears for Xcode 26+ builds.