So, I have this very simple class here to fetch some stuff from Core Data, which gives me concurrency warnings and I'm not sure how to approach this. The class is observable and the transactions can be displayed in a SwiftUI view and also be used from my cloud sync class. Is my fixed code a good way to fix those warnings?
The warnings occur on the context.perform function.
Sending main actor-isolated 'context' to nonisolated instance method 'perform(schedule:_:)' risks causing data races between nonisolated and main actor-isolated uses
Sending main actor-isolated value of non-Sendable type '() throws -> [NSPersistentHistoryTransaction]' to nonisolated instance method 'perform(schedule:_:)' risks causing races in between main actor-isolated and nonisolated uses
My specific pain points are:
- Is using
nonisolated
safe here? - Is there no better way to set
transactions
other than with a function? - Is there no better way than using
let context = await self.context
?
Original code:
@Observable
class PersistentHistoryFetcher {
var transactions: [NSPersistentHistoryTransaction] = []
func fetch() async throws {
let fetchRequest =NSPersistentHistoryChangeRequest.fetchHistory(after: lastToken)
let transactions = try await context.perform {
guard let historyResult = try self.context.execute(fetchRequest) as? NSPersistentHistoryResult, let history = historyResult.result as? [NSPersistentHistoryTransaction] else {
throw Error.historyTransactionConversionFailed
}
return history
}
self.transactions = transactions
}
"Fixed" code that produces no warnings:
@Observable
class PersistentHistoryFetcher {
var transactions: [NSPersistentHistoryTransaction] = []
nonisolated
func fetch() async throws {
let fetchRequest = await NSPersistentHistoryChangeRequest.fetchHistory(after: lastToken)
let context = await self.context
let transactions = try await context.perform {
guard let historyResult = try context.execute(fetchRequest) as? NSPersistentHistoryResult, let history = historyResult.result as? [NSPersistentHistoryTransaction] else {
throw Error.historyTransactionConversionFailed
}
return history
}
await updateTransactions(transactions)
}
func updateTransactions(_ transactions: [NSPersistentHistoryTransaction]) {
self.transactions = transactions
}