Swift
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Optionals
-
Section 4: Object-Oriented and Value Types
-
Section 5: Memory Management
-
Section 6: Generics and Error Handling
-
Section 7: Concurrency
-
Section 8: Working with Collections
-
Section 9: Codable and Data Handling
-
Section 10: Protocol-Oriented Programming
-
Section 11: Testing and Tooling
-
Section 12: Practical Projects
-
Section 13: Interview Practice
-
Section 14: More Practice Exercises
-
Section 15: More Standard Library
-
Section 16: Advanced Concurrency
-
Section 17: Foundation Framework Deep Dive
-
Section 18: URLSession and Networking Deep Dive
-
Section 19: Combine Framework
-
Section 20: SwiftUI Fundamentals for Swift Developers
-
Section 21: Server-Side Swift with Vapor
-
Section 22: Swift Package Manager Deep Dive
-
Section 23: Swift Concurrency Deep Dive
-
Section 24: More Language Features
-
Section 25: Error Handling Deep Dive
-
Section 26: Testing Deep Dive
-
Section 27: Data Structures and Algorithms in Swift
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Swift Macros (Swift 5.9+)
-
Section 31: Property Wrappers Ecosystem
-
Section 32: Swift Interop Deep Dive
-
Section 33: iOS App Architecture Patterns
-
Section 34: Performance and Debugging
-
Section 35: App Distribution and CI/CD
-
Section 36: More Practical Projects
-
Section 37: SwiftData and Persistence
-
Section 38: More Design Patterns
-
Section 39: More Review and Practice
-
Section 40: More Foundation Deep Dive
-
Section 41: Advanced Collections in Swift
-
Section 42: Advanced Generics Practice
-
Section 43: UIKit for Legacy and Hybrid Apps
-
Section 44: watchOS and visionOS Development Basics
-
Section 45: More Networking Patterns
-
Section 46: More Testing Practice
-
Section 47: Accessibility in Swift Apps
-
Section 48: Localization
-
Section 49: More Practical Projects Round 2
-
Section 50: Swift Charts Framework
-
Section 51: More Interview and Algorithm Practice
-
Section 52: Final Practice and Mastery
-
Section 53: Swift Compiler and Build System
-
Section 54: More Concurrency Practice
-
Section 55: App Store Guidelines and Review
-
Section 56: More Design and Architecture
199: UI Testing with XCUITest In Depth
I’ve spent a lot of time reviewing PRs for UI tests, and there is one misconception that keeps popping up: the belief that XCUITest is essentially a "macro recorder" for your app's visual labels. I see developers writing tests that look for app.buttons["Submit Order"] or app.staticTexts["Welcome, User!"]. On the surface, it feels intuitive because that's what the user sees. But in reality, this is the fastest way to create a "flaky" test suite that breaks the moment a copywriter changes a word or you localize your app into Spanish.
Imagine you're building a checkout screen. You have a button that says "Confirm Purchase." If you write app.buttons["Confirm Purchase"].tap(), your test passes today. But tomorrow, the product manager decides it should say "Complete Order" to increase conversions. Suddenly, your CI pipeline is red, not because the feature is broken, but because a string changed. That's a maintenance nightmare I don't want you to inherit.
Stop Testing Labels, Start Testing Identifiers
The professional way to handle this is by decoupling the visual representation of a UI element from its identity in the test. We do this using the accessibilityIdentifier. This property is invisible to the end user and doesn't affect the voice-over experience, but it provides a rock-solid hook for XCUITest.
// In your App Code (ViewController or SwiftUI View) confirmButton.accessibilityIdentifier = "checkout_confirm_button" // In your UI Test let app = XCUIApplication() app.buttons["checkout_confirm_button"].tap()Now, your copywriter can change the button text to "Buy Now," "Yes, Please!", or "Confirmar Compra," and your test won't blink. I always tell my juniors: if you're using a user-facing string in a
XCUIElementQuery, you're writing a test for the copy, not the functionality.Killing Flakiness with Explicit Waiting
Another deep-dive topic we need to cover is timing. UI tests are notoriously slow and unpredictable because they rely on the OS to render views and the network to return data. If you try to tap a button that hasn't appeared yet because a network request is still pending, XCUITest will throw an "Element not found" error and crash your test.
Don't use
Thread.sleep(). Seriously, don't. It's a blunt instrument that makes your tests take forever. Instead, usewaitForExistence(timeout:). This allows the test to proceed the millisecond the element appears, but gives it a reasonable window to show up before failing.let successMessage = app.staticTexts["order_success_label"] // Give the server up to 5 seconds to respond and the UI to update if successMessage.waitForExistence(timeout: 5) { XCTAssertTrue(successMessage.exists) } else { XCTFail("The order success message never appeared.") }Organizing with the Page Object Pattern
As your app grows, you'll find yourself repeating the same queries in ten different tests. If the hierarchy of your "Profile" screen changes, you'll have to update ten tests. To avoid this, I use a simplified Page Object Pattern. Essentially, you create a struct or class that represents a screen and houses all the queries for that screen.
struct CheckoutPage { let app = XCUIApplication() var confirmButton: XCUIElement { app.buttons["checkout_confirm_button"] } var totalAmountLabel: XCUIElement { app.staticTexts["checkout_total_label"] } func confirmPurchase() { confirmButton.tap() } } // In your actual test method: func testSuccessfulCheckout() { let checkoutPage = CheckoutPage() XCTAssertTrue(checkoutPage.totalAmountLabel.exists) checkoutPage.confirmPurchase() }This makes your tests read like a story rather than a list of coordinates and identifiers. It's cleaner, and more importantly, it's maintainable.
📋 Practical Task
Hardening the E-Commerce Checkout Flow
You are tasked with fixing a brittle UI test for a simulated Shopping Cart app. Currently, the test fails whenever the network is slightly slow or when the "Pay Now" button text is changed for a holiday promotion.
Your requirements:
- Modify the app code to add a stable
accessibilityIdentifierto the "Pay Now" button (call it"payment_submit_button") and the "Order Confirmed" label (call it"order_confirmation_label"). - Rewrite the
testPaymentFlow()method to stop relying on the string "Pay Now". - Implement
waitForExistence(timeout:)to ensure the "Order Confirmed" label is present before the test asserts its existence, accounting for a simulated 2-second network delay. - Refactor the test using a
CartPageobject to encapsulate the button and label queries.
There are no comments for now.