I am looking to resolve the concurrency warnings that I am getting after enabling the complete concurrency check in Xcode.
Here is a simple example to illustrate the warning. Consider I have a ContentView that has a long running task on the background thread. When I call the long running task inside that of a Task {...}
I get a "Passing argument of non-sendable type 'ContentView' outside of main actor-isolated context may introduce data races" warning. I get this warning when I enable the concurrency settings to complete in Xcode.
I tried the suggestion to make the ContentView Sendable but it generates additional warnings(see second image below) and will not work in my situation.
How do I get around this? appreciate any help/direction
See code below:
struct ContentView: View {
@State private var disable: Bool = false
@State private var value: Double = 0
var body: some View {
VStack {
Button {
disable = true
} label: {
Text("Press")
}
.task {
await value = self.fetchValue() //<---Here is the warning. See image below
}
Text("\(value)")
}
}
}
extension ContentView {
func fetchValue() async -> Double {
//long running task
try? await Task.sleep(nanoseconds: 60)
return 2.0
}
}
Here is the warning:
A SwiftUI View
is not attached to an actor
the body
is decorated with @MainActor
but not the View
itself.
When you have func
in the View
you can decorate the View
with @MainActor
the same way you would a "ViewModel".
import SwiftUI
@MainActor // Decorate the View
struct AsyncTest: View {
@State private var isRunning: Bool = false
@State private var value: Double = 0
var body: some View {
VStack {
Button {
print("press")
isRunning = true
} label: {
Text("Press")
}.disabled(isRunning)
.task(id: isRunning) {
guard isRunning else {return}
print("start")
value = await fetchValue()
print("end")
isRunning = false
}
if isRunning {
ProgressView()
}
Text("\(value)")
}
}
}
extension AsyncTest {
func fetchValue() async -> Double {
await Task.detached(priority: .userInitiated) {
// Mock long runing task
_ = (0...10000000).map { n in
n.description
}.sorted(using: KeyPathComparator(\.description))
//long running task
//try? await Task.sleep(nanoseconds: 60)
return 2.0
}.value
}
}
#Preview {
AsyncTest()
}