commit 70268eec5c5389ed8af7cb346c39f30604de3e08
parent 7e228e4a0c81ad512e1707a0c1a4e53efb51db4d
Author: Michael Camilleri <[email protected]>
Date: Sun, 14 Jun 2026 14:12:04 +0900
Fix main-actor isolation warnings in Core Data background closures
Three call sites reached main-actor-isolated state from inside a
background Core Data context closure (perform/performAndWait), which the
compiler flags as a potential data race under strict concurrency:
- InviteCoordinator.consumeStaleInvites read identity.currentID inside
the performAndWait closure and called the main-actor-isolated
staleInviteRecordNames. This commit hoists currentID into a local
read on the main actor before the closure, and marks
staleInviteRecordNames nonisolated since it only touches the context
and its value-type parameters.
- GameStore.cacheRemoteJournals logged a save failure via the main-actor
EventLog from inside ctx.perform. This commit builds the message on
the background queue, then hops to the main actor to call note().
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Diffstat:
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/Crossmate/Persistence/GameStore.swift b/Crossmate/Persistence/GameStore.swift
@@ -891,7 +891,10 @@ final class GameStore {
do {
try ctx.save()
} catch {
- self.eventLog?.note("GameStore: replay cache save failed — \(error)", level: "error")
+ let message = "GameStore: replay cache save failed — \(error)"
+ Task { @MainActor [weak self] in
+ self?.eventLog?.note(message, level: "error")
+ }
}
}
}
diff --git a/Crossmate/Services/InviteCoordinator.swift b/Crossmate/Services/InviteCoordinator.swift
@@ -440,12 +440,13 @@ final class InviteCoordinator {
}
guard !candidates.isEmpty else { return pings }
+ let currentAuthorID = identity.currentID
let ctx = persistence.container.newBackgroundContext()
let staleNames: Set<String> = ctx.performAndWait {
Self.staleInviteRecordNames(
among: candidates,
in: ctx,
- currentAuthorID: identity.currentID
+ currentAuthorID: currentAuthorID
)
}
guard !staleNames.isEmpty else { return pings }
@@ -462,7 +463,7 @@ final class InviteCoordinator {
return pings.filter { !staleNames.contains($0.recordName) }
}
- static func staleInviteRecordNames(
+ nonisolated static func staleInviteRecordNames(
among pings: [Ping],
in ctx: NSManagedObjectContext,
currentAuthorID: String?