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
200: Test Plans and Test Configurations
So, you've got a decent suite of tests now. But as your project grows, you're probably starting to feel the "Cmd+U drag." You hit that shortcut, go grab a coffee, and by the time you come back, the tests are done. It's a productivity killer. I've spent way too many hours waiting for a full suite to run just to verify a one-line change in a helper function.
The "Run Everything" Problem
Right now, we're likely using the default Xcode scheme. When you run tests, Xcode just grabs every single test target and runs every single test method. Let's look at our current setup for this WeatherApp project. We have WeatherParserTests (blazing fast), APIIntegrationTests (slow, hits the network), and WeatherUITests (glacial).
I tried running all of them this morning. It took nearly three minutes. Most of that time was spent waiting for the simulator to boot for the UI tests and waiting for the API to respond. I don't need to know if the UI is perfectly aligned every time I change a regex in the parser. I just want the parser tests to pass. I could manually disable targets in the Scheme editor, but that's tedious and doesn't scale if I'm working with a team.
Moving to a Test Plan
This is where Test Plans come in. Instead of the Scheme being the "source of truth" for what runs, we move that logic into a separate .xctestplan file. This lets us define different "flavors" of test runs.
I'll go to the Product > Scheme > Edit Scheme... menu, click on Test in the sidebar, and instead of "Use default test settings," I'll click the + button to create a new Test Plan. I'll call this FastCheck.xctestplan.
// In the Xcode UI for FastCheck.xctestplan:
// [x] WeatherParserTests
// [ ] APIIntegrationTests <-- Uncheck this
// [ ] WeatherUITests <-- Uncheck this
Now, when I select the "FastCheck" plan from the scheme dropdown and hit Cmd+U, it finishes in under two seconds. I've essentially created a "Smoke Test" suite. But wait—what happens when I actually do need to check the API? I don't want to keep clicking checkboxes. I'll just create a second plan called FullRegression.xctestplan and enable everything there. Now I have a toggle for "Dev mode" vs "CI mode."
Tuning the Environment
Here is where it gets interesting. Sometimes a test fails, but only in a weird, non-deterministic way. I suspect a race condition in our weather cache. I could turn on the Thread Sanitizer (TSan) for the whole project, but that slows down everything and might trigger false positives in third-party libraries.
I'll open a new plan called DebugConcurrency.xctestplan. Inside this plan, I can go to the Configurations tab. Instead of changing the global build settings, I'll specifically enable the Thread Sanitizer just for this plan.
- Observation: I run the
FullRegressionplan. Everything passes. - Observation: I switch to the
DebugConcurrencyplan. Suddenly, the console screams about a data race inWeatherCache.swift.
The magic here is that I didn't have to change my project settings or rebuild the entire app from scratch with a different scheme. The test plan handles the environment configuration. I can even set specific Arguments Passed On Launch or environment variables (like -USE_MOCK_SERVER YES) for one plan without affecting the others.
Picking the Right Tool for the Job
If you're just starting a project, a single scheme is fine. But the moment you have tests that take more than 10 seconds, or tests that require different runtime environments (like a "Stress Test" plan that runs the same tests 100 times to find flakes), you need these plans. It stops the "test fatigue" where developers just stop running tests because they take too long.
📋 Practical Task
Exercise: Architecting a Multi-Tier Test Suite for a Currency Converter
You are working on a CurrencyConverter app. You have three test targets: ConversionLogicTests (Unit tests), ExchangeRateAPITests (Integration tests), and ConversionUITests (UI tests). Your goal is to set up a professional testing workflow using Test Plans.
- Create a new Test Plan named
Developer.xctestplan. Configure it to only runConversionLogicTeststo ensure a near-instant feedback loop. - Create a second Test Plan named
Nightly.xctestplan. Configure it to run all three test targets. - In the
Nightly.xctestplan, navigate to the Configurations section and enable the Address Sanitizer to catch memory corruption issues that might only appear during long-running test suites. - Add an Environment Variable to the
Nightly.xctestplancalledAPI_TIMEOUTwith a value of60, ensuring the integration tests don't fail prematurely on slow CI servers. - Verify your setup by switching between the plans in the Xcode Scheme menu and observing which targets are executed in the Test Navigator.
There are no comments for now.