Quick / Nimble

A Matcher Framework for Swift and Objective-C
https://quick.github.io/Nimble/documentation/nimble/
Apache License 2.0
4.81k stars 600 forks source link

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

Open siddarthkalra opened 8 months ago

siddarthkalra commented 8 months ago

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' returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary

// Passing argument of non-sendable type 'Matcher' 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:

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

younata commented 8 months ago

This is being worked on for the next major version of Nimble. There's a lot involved in this, though. Especially to keep a decent experience when using with Quick.

siddarthkalra commented 8 months ago

@younata that's great to hear. I'll continue to use @preconcurrency import for now then. Any idea what kind of time horizon, we are looking at for this release?

younata commented 8 months ago

Any idea what kind of time horizon, we are looking at for this release?

I'd like to get Nimble 14 out before September, with betas running during the summer. No promises though - I'm not going to burn myself out for a hobby project.

siddarthkalra commented 8 months ago

Thanks @younata. Totally understand. Just a rough estimate is helpful. Really appreciate your work on this project 🙏