From 1eb0dcbf56b24ffa2305a80d6cc112293ed9875c Mon Sep 17 00:00:00 2001 From: Brunelli Cupello Date: Fri, 21 Aug 2026 15:36:30 -0300 Subject: [PATCH] fix: stop the Sparkle background task tripping an executor check The closure handed to `backgroundQueue.schedule` is non-Sendable, so under Swift 6 it inherits the enclosing `MainActor` isolation and the compiler emits an executor check at its entry. It then runs on `backgroundQueue`, where that check fails -- on Xcode 26.6 the app traps about two seconds after launch: _dispatch_assert_queue_fail swift_task_isCurrentExecutorWithFlagsImpl closure #1 in SparkleUpdateController.manageBackgroundTask(_:) Marking it `@Sendable` makes it properly non-isolated. The hop to the main actor is the `Task` inside it, which is where the hop belongs -- so this is a correctness fix regardless of whether a given toolchain turns the check into a trap or a warning. Co-Authored-By: Claude Opus 5 --- Mythic/Utilities/SparkleUpdateController.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Mythic/Utilities/SparkleUpdateController.swift b/Mythic/Utilities/SparkleUpdateController.swift index 5bd0fbd7..56172f95 100644 --- a/Mythic/Utilities/SparkleUpdateController.swift +++ b/Mythic/Utilities/SparkleUpdateController.swift @@ -52,7 +52,12 @@ final class SparkleUpdateController: NSObject, SPUUserDriver, ObservableObject { backgroundQueue.schedule( after: .init(.now()), interval: .seconds(60 * 60 * 6) - ) { + ) { @Sendable in + // @Sendable is load-bearing: without it this non-Sendable closure inherits the + // enclosing MainActor isolation, so Swift 6 emits an executor check at its entry. + // It then runs on `backgroundQueue`, dispatch_assert_queue fails, and the app traps + // on launch (EXC_BREAKPOINT on the BackgroudEventService queue). The hop to the main + // actor is the Task below, which is where it belongs. Task { @MainActor in SparkleUpdateController.shared.checkForUpdates(userInitiated: false) }