Hi all,
I am struggling to identify what is the best solution to pass non-sendable from nonisolated context to a call which is nonisolated but hold by an isolated object.
import SwiftUI
import PlaygroundSupport
// We cannot and do not want to conform it to Sendable
class NonSendable {
var name: String
init(name: String) {
self.name = name
}
}
protocol Servicing {
func longOP(model: NonSendable) async -> NonSendable
}
final class Service: Servicing {
func longOP(model: NonSendable) async -> NonSendable {
try? await Task.sleep(nanoseconds: 1_000_000) // Assume this takes a long time and is synchronous
model.name = " serviced"
return model
}
}
@MainActor
@Observable class Store {
var model: NonSendable?
let service: Servicing = Service()
func loadModel(named name: String) async {
model = NonSendable(name: name)
await service.longOP(model: model!) // Error: Non-sendable result type 'NonSendable' cannot be sent from nonisolated context in call to instance method 'longOP(model:)'
}
}
struct ContentView: View {
@State private var store = Store()
var body: some View {
Text("hello \\(store.model?.name)!")
.frame(width: 600)
.task {
await store.loadModel(named: "friends")
}
}
}
PlaygroundPage.current.setLiveView(ContentView())
A solution: Making Servicing isolated to the MainActor
But why Service/Servicing should be isolated to the MainActor? It could also have some parameters which have to work on a nonisolated context, so only forwarding the problem to the next object.
@MainActor
protocol Servicing {
func longOP(model: NonSendable) async -> NonSendable
}
final class Service: Servicing {
func longOP(model: NonSendable) async -> NonSendable {
async let result = longOP()
model.name += await result
return model
}
private nonisolated func longOP() async -> String {
try? await Task.sleep(nanoseconds: 3_000_000_000) // Assume this takes a long time and is synchronous
return "serviced"
}
}
I did try to use the sending
keyword, or isolation splitting but I could not find a solution with them.
What would be the best approach to solve that?