Question about swift 6.2 Concurrency

Hello. I am trying out Approachable Concurrency with default actor isolation set to MainActor.

I have couple of local SPM packages which also have Approachable Concurrency and default actor isolation enabled. Swift tool version is set to 6.2

.enableExperimentalFeature("StrictConcurrency"),
.defaultIsolation(MainActor.self),
.enableUpcomingFeature("NonisolatedNonsendingByDefault"),
.enableUpcomingFeature("InferIsolatedConformances"),

The problem is that I get error Main actor-isolated property 'id' can not be referenced from a nonisolated context when trying to access a struct which is placed in the SPM local package. If you copy that struct to the main target, error goes away.

public struct Item: Sendable, nonisolated Equatable {
  public let id: String
  public let title: String

  public init(
    id: String = UUID().uuidString,
    title: String = "",
  ) {
    self.id = id
    self.title = title
  }
}

I have a few questions I would like to ask about;

  • Does writing nonisolated let for title and id instead of let resolve the error?
  • Where is the error being indicated
  • Do you also get a similar error for title?
    • If so what function is it in?
  • Does removing nonisolated from nonisolated Equatable resolve the error?
  • Does removing the Equatable conformance resolve the error?
  • Try implementing == manually. What errors are returned?
  • Are the provided settings present across your targets?
  • How do you use Item.id in the affected target?
Technical Reasons for these questions

One of the things that I would focus on is what nonisolated Equatable is doing to your code.

Equatable performs conformance synthesis if all containing types conform to Equatable, this allows for developers to more quickly create an Equatable conforming Type. By indicating that Equatable is nonisolated we are expecting for the Equatable synthesizer to generate a nonisolated function. This seems to indicate that we can use nonisolated on id’s declaration to make sure that swift is making id with nonisolated functionality.

After that I would look for every usage of Item.id to find any usages that are in nonisolated code blocks.

Hello. Thanks for replying.

Does writing nonisolated let for title and id instead of let resolve the error?

Yes, marking struct or title and id as nonisolated resolves the issue. Error is indicated in the main target, outside of SPM package, when I try to use Item struct inside of a @concurrent closure.

Do you also get a similar error for title?

Yes. It’s a @concurrent closure. Here is the gist of DataLoadingState. The @concurrent closure is convertAsync. Here is how it’s used inside the ViewModel.

private func didRecieve(dataState: FetchCategoriesWorker.Output) async {
    self.dataState = dataState

    let testState = DataLoadingState.loaded([Item(), Item()])

    let _ = await testState.convertAsync { value in
      return value.map { item in
        return Item(id: item.id, title: item.title)
      }
    }

    let convertedState2 = dataState.convert { value in
      return value.data.map {
        let title = $0.title
        let id = $0.id
        return RegularCellConfiguration(id: $0.id, title: $0.title)
      }
    }

    await stream.send(.fetchingStateChanged(state: convertedState2))
}

Does removing nonisolated from nonisolated Equatable resolve the error?
Does removing the Equatable conformance resolve the error?

No, remove nonisolated does not fix the issue. Manually implementing == results in the error Conformance of 'Item' to protocol 'Equatable' crosses into main actor-isolated code and can cause data races. This error is solved by marking == as nonisolated or adding @MainActor conformance.

Are the provided settings present across your targets?

Yes, I have same settings for all local SPM packages and main target has same settings too. The item is not used anywhere in the SPM package or main target except that single place. I fetch remote data and try to map it to the different struct. I was just experimenting with UI data binding and things like that.

Yesterday I was able to solve the issue by removing default MainActor isolation from Networking and Domain SPM packages. I’m just curious why copying Item struct from SPM package to main target resolves the error when both of them are isolated to the MainActor by default and share same settings?