Today’s example shows a static property providing the current date.

Perfect mechanism when we don’t want to inject it everywhere where it’s used.

How to test it? Check it out ⤵️

Initial setup

let currentDateFormatter: DateFormatter = {
    let formatter = DateFormatter()
    formatter.dateStyle = .short
    formatter.timeStyle = .none
    return formatter
}()

var currentDateFormatted: String {
    currentDateFormatter.string(from: DateEnvironment.currentDate())
}

enum DateEnvironment {
    static var currentDate: () -> Date = Date.init
}

extension Date {
    // 16.04.2025
    static let sixteenthOfApril = Date(timeIntervalSince1970: 1744840515)
}

Old way - XCTest

  • Override the static property in tests.
  • Works, but not concurrency-safe (Ok, for XCTest because test ran serially).
class CurrentDateFormatterTests: XCTestCase {
    func test_dateFormatting() { // ✅
        DateEnvironment.currentDate = { .sixteenthOfApril }
        XCTAssertEqual(currentDateFormatted, "16.04.2025")
    }
}

Swift Testing before Swift 6.1

enum DateEnvironment {
    @TaskLocal static var currentDate: () -> Date = Date.init
}

struct CurrentDateFormatterTests {
    @Test
    func dateFormatting() { // ✅
        DateEnvironment.$currentDate
            .withValue({ .sixteenthOfApril }) {
                #expect(currentDateFormatted == "16.04.2025")
            }
    }
}

Why @TaskLocal?

  • Scoped per async Task.
  • No global side-effects
  • Safe for parallel tests

What is @TaskLocal?

  • Property wrapper for per-Task storage.
  • Default value is returned when no override is applied.
  • withValue(_:, operation:) - binds a new value just for that Task.
  • Child Task { … } inherits override.
  • Detached - Task.detached { … } does not inherit override.
DateEnvironment.$currentDate.withValue({ .sixteenthOfApril }) {
    print(DateEnvironment.currentDate())        // 16.04.2025
    Task { 
        print(DateEnvironment.currentDate())    // 16.04.2025 (inherited)
    }
    Task.detached { 
        print(DateEnvironment.currentDate())    // today’s date (no inheritance)
    }
}

New in Swift 6.1: Test Scoping in Action

enum DateEnvironment {
    @TaskLocal static var currentDate: () -> Date = Date.init
}

struct FixedDateTrait: TestTrait, TestScoping {
    let date: Date

    func provideScope(
      for test: Test, 
      testCase: Test.Case?,
      performing function: () async throws -> Void
    ) async throws {
        try await DateEnvironment.$currentDate
          .withValue({ date }, operation: function)
    }
}

extension Trait where Self == FixedDateTrait {
    static func fixedDate(_ date: Date) -> Self { 
        .init(date: date) 
    }
}

struct CurrentDateFormatterTests {
    @Test(.fixedDate(.sixteenthOfApril))
    func dateFormatting() { // ✅
        #expect(currentDateFormatted == "16.04.2025")
    }
}

Final advice

Stay Curious

Use @TaskLocal and Test Scoping for deterministic, parallel-safe tests. Enjoy faster feedback looks and cleaner code.

Remember - Learning never stops - keep experimenting with new features!

PDF version ⤵️

Concurrency-Safe Testing in Swift 6.1 with @TaskLocal and Test Scoping


Thanks for reading. 📖

I hope you found it useful!

If you enjoy the topic don’t forget to follow me on one of my social media - LinkedIn, X, Mastodon, Bluesky or via RSS feed to keep up to speed. 🚀