I think it’s solved in nightly builds now, so should be in the next beta.
Has the adoption of @MainActor
changed in some way regarding XCTestCase
since this?
I'm running the Xcode 16.1 beta and trudging through issues with our tests, many of which were originally done to get around this. We have some setUp
overrides marked @MainActor
where the entire test case subclass would have been marked @MainActor
prior to Swift 5.10, however now it seems to complain instead about marking setUp
as @MainActor
, but not the subclass. What is the direction here?
XCTestCase
is not actor-isolated, nor do we plan to change that. It sounds like you're running into a newer, stricter concurrency diagnostic added or enforced in Swift 6. If you could share the full diagnostic output, we may be able to help you resolve the issue.
I have some setUp
methods declared @MainActor
to call something that is main actor isolated. But in Xcode 16.0 I am getting the warning:
Main actor-isolated instance method 'setUp()' has different actor isolation from nonisolated overridden declaration; this is an error in the Swift 6 language mode
/Users/…/ViewControllerPresentationSpy/SwiftSample/Tests/ViewControllerAlertTests.swift:14:19: warning: main actor-isolated instance method 'setUp()' has different actor isolation from nonisolated overridden declaration; this is an error in the Swift 6 language mode
override func setUp() {
^
XCTest.XCTest:17:15: note: overridden declaration is here
open func setUp()
^
How should I resolve set-up situations like this? This example is in GitHub - jonreid/ViewControllerPresentationSpy: Unit test presented and dismissed iOS view controllers, including alerts and action sheets, using the project in the SwiftSample folder.
If I remove the @MainActor
declaration, I get a number of issues around calling UIKit at all, such as:
Call to main actor-isolated instance method 'loadViewIfNeeded()' in a synchronous nonisolated context; this is an error in the Swift 6 language mode
@jonreid here is a similar issue being tracked. I do not know if there is a workaround.
Thanks, @vanvoorden! Happy to continue the discussion here or there.
I think you can work around the issue by favoring the async throws
version of setUp
, which will allow you to await
actor-isolated functionality.
@jonreid In addition turning on the upcoming feature for "global actor usability" will allow you to make a subclass of XCTestCase
a @MainActor
.
The only thing I don't understand is, if you then try to access a property on your subclass from within the async throws
version of setUp
it requires self
to be Sendable
, which the compiler will let you do, but seems like SE-0434 would suggest that should result in an error.
I was able to move forward under complete concurrency checking (Swift 5 language mode) by turning on that upcoming flag, moving the @MainActor
back to the subclass as a whole, and convert to use the async throws
version of setUp
and marking the subclass Sendable
. This lets you access self
and all of its properties without an await
because an async
function on a @MainActor
class is considered bound to the @MainActor
(unless on an extension or whatnot).
In Xcode 16.1 through 16.2b3 I'm seeing that annotating an XCTestCase subclass with @MainActor
is allowed under Swift 6 language mode with Complete strict concurrency checking. Can someone confirm if this is intentional or a temporary bug?
For example, this compiles and runs without errors and the test passes.
@MainActor final class TestActorExampleTests: XCTestCase {
func testExample() throws {
let viewController = UIViewController()
viewController.title = "test"
XCTAssertEqual(viewController.title, "test")
}
}