I am trying to create a full SwiftUI Document app (using swift 6, as it matters).
Apple doc says that the Document init(configuration:)and fileWrapper(configuration:)are called on a background thread - and I checked they are.
However, the Document holds a model which is @ObservableObjecttherefore bound to @MainActor.
To write to a file, because fileWrapper(configuration:)is called directly without any chance to serialise the modelin the MainActorI used a function that goes back to the main actor, serialises the data, and then comes back to the background thread for saving.
Clearly cumbersome, not ideal, but works fine.
Here is the code of that function:
nonisolated
public func runOnMainAndWait<T: Sendable>(_ operation: @escaping @Sendable @MainActor () -> T) -> T {
// We are in the caller execution context, create a semaphore to wait for
let semaphore = DispatchSemaphore(value: 0)
// Declare the variable to store the result
var result: T! = nil
// Create a task that will execute in the caller execution context, whenever the caller
// suspends itself, a bit later on.
Task {
result = await MainActor.run {
// Execute the operation to convert MainActor bounded
return operation()
}
// We're back into caller execution context, we can manipulate the semaphore, and
// unblock the second part of the method
semaphore.signal()
}
// Here, we suspend the caller, so the above task will execute, storing its computation in
// `result`, and unblock us.
semaphore.wait()
// Return the result
return result
}
To read a file, however, the things get even more complex. I can't just read the file data in the background, and then use runOnMainAndWaitbecause I need a Factory... which I can't pass as a parameter...
So, I store the file data in the document, and transform it into modelon the main actor later on when I get a chance... which is the creation of the main view.
Surely, this is by far too complex and cumbersome. There must be something I missed.
What is the pattern to use for SwiftUI Document App ?