Hi everyone,
I’m trying to write a view extension similar to the task(id:priority:_) modifier that can accept several ids instead of just one.
In order to do that, I first wrote an EquatableTuple struct like this :
private struct EquatableTuple<each T: Equatable>: Equatable {
let values: (repeat each T)
init(_ values: repeat each T) {
self.values = (repeat each values)
}
static func == (lhs: Self, rhs: Self) -> Bool {
for isEqual in repeat each lhs.values == each rhs.values {
guard isEqual else {
return false
}
}
return true
}
}
Then the view extension itself :
public extension View {
nonisolated func task<each T: Equatable>(id values: repeat each T, priority: TaskPriority = .userInitiated, @_inheritActorContext _ action: @Sendable @escaping () async -> Void) -> some View {
return task(id: EquatableTuple(repeat each values), priority: priority, action)
}
// Redundant simple variant for 2 ids without the each syntax to illustrate the problem below
nonisolated func task<T1: Equatable, T2: Equatable>(id1 value1: T1, id2 value2: T2, priority: TaskPriority = .userInitiated, @_inheritActorContext _ action: @Sendable @escaping () async -> Void) -> some View {
return task(id: EquatableTuple(value1, value2), priority: priority, action)
}
}
Everything compiles fine but a problem arises when I try to use the view extension :
@MainActor
struct TestView: View {
let a: Int
let b: Int
@State private var c = 0
var body: some View {
Text("Test")
.task(id: a, b) {
c = 1 // ERROR : Main actor-isolated property 'c' can not be mutated from a Sendable closure
}
.task(id1: a, id2: b) {
c = 2 // Compiles fine, as expected
}
}
}
It looks like the first task extension with the repeat syntax isn't considering the @_inheritActorContext attribute of the closure and so the closure { c = 1 } in the view isn't inferred to be on the main actor.
Am I doing something wrong here ? Or is it a bug from the swift compiler ?