Xcode 27 Swift Testing: repeat tests until they pass or fail with swift test

How --repeat-until and --maximum-repetitions work in Swift 6.4, tested on Xcode 27.1, and how to use them to reproduce flaky tests.

Read time
10 min
Word count
1.4K
Sections
11
FAQs
8
Share
A developer desk with Xcode and VS Code on two monitors, a laptop and an iPhone running a plant care app.
On this page · 11 sections
  1. The problem with flaky tests
  2. What changed in Xcode 27 and Swift 6.4
  3. What we saw when we ran it
  4. Using --repeat-until fail to reproduce a failure
  5. Using --repeat-until pass with care
  6. Which tests are repeated
  7. A known issue in Swift 6.4.0
  8. A practical debugging workflow
  9. What this feature does not solve
  10. How eCorpIT can help
  11. References

Summary. Swift 6.4, which ships in Xcode 27, adds two options to swift test: --repeat-until pass or --repeat-until fail, and --maximum-repetitions. Each Swift Testing test case then repeats on its own until it meets the condition or reaches the limit. The options work only for Swift Testing tests, not XCTest. The limit counts the first run. Without a limit, --repeat-until has no upper bound. In our own test run on Xcode 27.1, a test that failed twice and then passed still left swift test reporting failure. This guide explains how the options behave, what we saw and how to use them to chase flaky tests.

Flaky tests are hard to diagnose because a single passing run does not prove the behaviour is stable. Re-running a failing test by hand helps, but it is slow and hard to standardise in command-line and CI workflows.

About this article. This guide is based on Apple's Xcode 27 release notes, the SwiftPM 6.4 release notes, Swift Testing proposal ST-0024, Apple's WWDC26 session on what's new in Swift, and our own test run with Xcode 27.1, as checked on 17 October 2026. Researched with AI assistance and reviewed by our editorial team.

The problem with flaky tests

A deterministic test gives the same result when its inputs and environment are the same. A flaky test can give different results across runs even though nothing has intentionally changed. Tests are often sensitive to:

  • asynchronous work and concurrency;
  • timing;
  • shared resources and file-system state;
  • external services and networking;
  • randomly generated data;
  • other environmental conditions.

Repetition is a test execution control, not a new assertion mechanism. It helps you see intermittent behaviour more often; it does not explain it.

What changed in Xcode 27 and Swift 6.4

Apple lists the change in the Xcode 27 release notes under Swift Package Manager, New Features: swift test now supports repeating test cases until a condition is met, using --maximum-repetitions and --repeat-until with a value of pass or fail, and only the test cases that match the repeat condition are repeated. Xcode 27 includes Swift 6.4, and the feature comes from the open-source Swift 6.4 toolchain: it was specified in Swift Testing proposal ST-0024 and is carried out by Swift Testing's own runner.

The command looks like this:


            swift test --repeat-until pass --maximum-repetitions 10
swift test --repeat-until fail --maximum-repetitions 100
          
Option What it does
--repeat-until pass A failing test case repeats until it passes or reaches the limit; passing cases run once
--repeat-until fail A passing test case repeats until it fails or reaches the limit
--maximum-repetitions N Sets the most times each case runs, counting the first run
--maximum-repetitions N on its own Runs every test case exactly N times
--repeat-until with no maximum No upper limit: cases repeat until they meet the condition

The SwiftPM 6.4 release notes describe the limit and the behaviour of each condition. Apple's WWDC26 session on what's new in Swift adds that when you repeat until the tests pass, only failing tests are re-run.

Swift Testing only

The help text for both options says they are supported only for Swift Testing test suites. XCTest test methods in the same swift test run are not repeated. If your package still has XCTest tests, migrate the flaky ones to Swift Testing before you rely on these options; Apple's WWDC26 session on migrating to Swift Testing covers how.

Swift Testing itself has been available since Xcode 16. There is no new @Test trait or attribute. ST-0024 lists a repetition trait only as a possible future direction, so repetition today is chosen on the command line.

What we saw when we ran it

We built a small Swift package with three Swift Testing tests and ran it with Xcode 27.1 (build 27A9275), whose toolchain reports Swift 6.4. One test failed on its first two runs and passed on the third, one always passed, and one failed only on its third run. Each test counted how many times it ran.

Command Fails twice, then passes Always passes Fails on third run Exit status
swift test 1 run 1 run 1 run Failure
--repeat-until pass --maximum-repetitions 5 3 runs 1 run 1 run Failure
--repeat-until fail --maximum-repetitions 5 1 run 5 runs 3 runs Failure
--maximum-repetitions 3 3 runs 3 runs 3 runs Failure
--filter alwaysPasses --repeat-until fail --maximum-repetitions 4 Not run 4 runs Not run Success

Four things stand out:

  • Each case repeats on its own. In the fail run, the test that failed first stopped at once while the others kept going.
  • The limit includes the first run. A maximum of 5 meant five runs in total, not one run plus five repeats.
  • A pass after retries is still a failure. With --repeat-until pass, the flaky test passed on its third run, but swift test still exited with failure because the earlier runs had failed. Do not use it to turn a red CI build green.
  • Fail mode repeats your healthy tests. Every passing test runs up to the limit, so narrow the run with --filter.

Using --repeat-until fail to reproduce a failure

This is the more useful mode for investigation. Point it at the suspect test and give it a generous limit:


            swift test --filter LoginTests --repeat-until fail --maximum-repetitions 100
          

Each matching test case keeps running until it fails or reaches 100 runs. Without --filter, every passing test in the package repeats up to the limit, which can turn a quick suite into a long one.

Once the failure appears, investigate the usual suspects:

  • concurrency behaviour and shared mutable state;
  • timing assumptions and asynchronous completion;
  • resource clean-up between tests;
  • test ordering;
  • environment-dependent behaviour.

The repetition turns a hard manual reproduction into a repeatable command. It does not find the root cause.

Using --repeat-until pass with care


            swift test --repeat-until pass --maximum-repetitions 10
          

This re-runs only failing test cases and shows whether a failure is intermittent. Two cautions apply. A test that eventually passes still fails the overall run, as our results show. And a pass after a retry is evidence of instability, not proof the test is healthy:


            Pass after retry
      ≠
Problem solved
          

Which tests are repeated

Repetition works per test case, not per suite or per run. For a parameterised test, each argument is its own test case, so only the argument that misbehaves repeats. A Swift Testing suite like this one is what the options act on:


            import Testing

@Suite struct LoginTests {
    @Test func validLogin() async throws { /* ... */ }
    @Test func invalidPassword() async throws { /* ... */ }
    @Test func expiredSession() async throws { /* ... */ }
}
          

The same change reached Xcode's test plans. The Xcode 27 release notes say the Test Repetition Mode setting now repeats only individual Swift Testing test cases. Xcode and xcodebuild have long offered their own repetition, through the Run Repeatedly command and the -test-iterations and -run-tests-until-failure options; what is new in Swift 6.4 is the swift test path for packages.

A known issue in Swift 6.4.0

Swift Testing has a reported bug in which warnings and known issues are treated as failures by the repetition logic, so pass mode can repeat when it should not and fail mode can stop early. The fix was merged to the 6.4.1 and 6.4.2 release branches. The toolchain in Xcode 27.1 reports itself as Swift 6.4 with a 6.4.0 build number, and Apple's release notes do not name the patch version, so expect this behaviour until a later toolchain ships. If your tests record warnings or use withKnownIssue, check the results by hand.

A practical debugging workflow

  1. Run the tests normally. If a test fails every time, investigate that failure directly.
  1. Reproduce the flake. Run swift test --filter <suspect> --repeat-until fail --maximum-repetitions 100.
  1. Capture the evidence. Keep the failure message, logs, the state of fixtures and any environment details.
  1. Make the test deterministic. Remove the timing, shared state or environmental dependency that caused it.
  1. Confirm the fix. Run the same repeat-until-fail command again and check it reaches the limit without failing.

What this feature does not solve

  • It does not find the root cause. If a test fails on run 37, you still have to work out why.
  • It does not make flaky tests reliable. It changes how often you see the behaviour, not its source.
  • It does not replace assertions. Your expectations still decide whether a test passes.
  • It does not mean every test should be retried. Retries add run time and can hide instability if used as a permanent workaround.
  • It does not cover XCTest. Only Swift Testing tests repeat.
  • It does not remove the need for isolation. Tests that depend on shared state or external services stay fragile until you fix the dependency.

Our Xcode 27 and Swift 6.4 overview covers the other changes in this release, and our guide to choosing a software testing partner covers test strategy more broadly.

How eCorpIT can help

eCorpIT is a Gurugram-based technology organisation, founded in 2021, assessed at CMMI Level 5 and MSME certified, with senior-led engineering teams working across AWS, Microsoft and Google platforms. Our iOS teams migrate test suites to Swift Testing, track down flaky tests and set up CI runs that use repetition for diagnosis without hiding failures. Talk to us at /contact-us/.

Last updated: 17 October 2026.

References

  1. Xcode 27 Release Notes — Apple Developer Documentation
  1. SwiftPM 6.4 Release Notes — Swift.org
  1. ST-0024: Test case repetition — Swift Evolution
  1. Swift 6.4 Released — Swift.org
  1. What's new in Swift, WWDC26 — Apple Developer
  1. Migrate to Swift Testing, WWDC26 — Apple Developer
  1. Swift Testing — Apple Developer Documentation
  1. Test.Case — Apple Developer Documentation
  1. Running tests and interpreting results — Apple Developer Documentation
  1. Repetition policy treats warnings and known issues as failures — swiftlang/swift-testing on GitHub

Frequently asked

Quick answers.

01 What is new in Xcode 27 for repeating package tests?
Swift 6.4, included in Xcode 27, adds --repeat-until with a value of pass or fail, and --maximum-repetitions, to swift test. Each matching Swift Testing test case repeats until it meets the condition or reaches the limit. Apple lists the change under Swift Package Manager in the Xcode 27 release notes.
02 Is this a new Swift Testing attribute?
No. There is no new @Test attribute or trait; you choose repetition on the command line. The behaviour was specified in Swift Testing proposal ST-0024 and is carried out by Swift Testing's runner, which is why it works only for Swift Testing tests. A per-test repetition trait is listed only as a possible future direction.
03 Does it work with XCTest?
No. The help text for both options says they are supported only for Swift Testing test suites, and XCTest methods in the same run are not repeated. To use repetition on a flaky XCTest test, migrate it to Swift Testing first, or use Xcode's own Run Repeatedly command or xcodebuild's test iteration options.
04 What does --repeat-until fail do?
It repeats each passing test case until that case fails or reaches the maximum number of runs. Each case is handled on its own, so one failure does not stop the others. Every passing test repeats up to the limit, so use --filter to target the suspect test and keep the run short.
05 What does --repeat-until pass do?
It re-runs only the test cases that fail, until each passes or reaches the maximum. Cases that pass first time run once. In our test with Xcode 27.1, a test that failed twice and then passed still left swift test reporting failure, so this mode shows instability rather than hiding it.
06 How does --maximum-repetitions count runs?
It sets the most times each test case runs, and the first run counts. In our test, a maximum of five gave five runs in total. Used on its own, without --repeat-until, it runs every test case exactly that many times. Used with --repeat-until, it caps how long each case can repeat.
07 What happens if I leave out --maximum-repetitions?
Then --repeat-until has no upper limit, and a case repeats until it meets the condition. A healthy test under repeat-until fail would run indefinitely, so always pass both options together, especially in CI, where a run without a limit can hold a build agent for a long time.
08 Can this fix a flaky test?
No. Repetition helps you reproduce and study intermittent behaviour, but the cause still needs fixing, whether that is shared state, timing, ordering or an external dependency. Use repeat-until fail to reproduce the problem, fix the test or the code, then run the same command again to check the failure no longer appears.

About the author

Aman Mathur

Software Engineer | Flutter

Software Engineer with 3+ years building high-performance, AI-powered cross-platform mobile apps for global clients.

Subscribe

One engineering note a week. No fluff, no spam.

Senior-architect playbooks on AI agents, mobile apps, cloud, security, data, and marketing — delivered every Wednesday.

Past the reading

Read enough. Let's build something.

A senior architect responds in 24 working hours with scope, indicative cost, and a timeline. NDA before any technical conversation.