Skip to content

Using Nimble types with @MainActor gives a warning when SWIFT_STRICT_CONCURRENCY=targeted due to lack of Sendable conformance #1127

@siddarthkalra

Description

@siddarthkalra
  • I have read CONTRIBUTING and have done my best to follow them.

What did you do?

I recently updated my project to use SWIFT_STRICT_CONCURRENCY=targeted. After doing so, Nimble is giving the following warnings when using toEventually():

// Non-sendable type 'SyncExpectation<FooViewModel.State>' returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary

// Passing argument of non-sendable type 'Matcher<FooViewModel.State>' outside of main actor-isolated context may introduce data races

Here's some code to reproduce the problem:

The subject being tested:

@MainActor final class FooViewModel {
    struct State: Equatable {
        let val: String
    }

    var state: State {
        State(val: "val")
    }
}

The Spec:

@MainActor final class ExampleTestSpec: AsyncSpec {
    override class func spec() {
        var fooVM: FooViewModel!

        beforeEach { @MainActor in
            fooVM = FooViewModel()
        }

        it("sets states") { @MainActor in
            // This works fine
            expect(fooVM.state).to(equal(FooViewModel.State(val: "val")))
        }

        it("eventually sets state") { @MainActor in
            // This gives two warnings:
            // Non-sendable type 'SyncExpectation<FooViewModel.State>' returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary
            // Passing argument of non-sendable type 'Matcher<FooViewModel.State>' outside of main actor-isolated context may introduce data races
            await expect(fooVM.state).toEventually(equal(FooViewModel.State(val: "val")))
        }
    }
}

Reasoning around @MainActor usage: Since we're testing a ViewModel here, we want to isolate everything to run on the main actor.

Please feel free to suggest an alternative way but right now, I think the only feasible workarounds available are:

  1. Use @preconcurrency import Nimble to silence the warnings
  2. Use @unchecked Sendable to silence the warnings

Neither of the above workarounds are ideal. I'm curious to understand why SyncExpectation and Matcher don't conform to Sendable. Is there a plan to add this conformance soon? If not then what approach would you suggest in this situation?

What did you expect to happen?

I expected no warnings.

What actually happened instead?

I got two warnings around SyncExpectation and Matcher being non-sendable types.

Environment

List the software versions you're using:

  • Quick: 7.3.0
  • Nimble: 13.1.2
  • Xcode Version: 15.1 (15C65)
  • Swift Version: 5.9.2

Please also mention which package manager you used and its version. Delete the
other package managers in this list:

  • Swift Package Manager: Swift Package Manager - Swift 5.9.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions