Modernize: Swift 6 language mode, Xcode 26 settings, Sparkle 2.9.6, CI build workflow - #981
Open
keithadler wants to merge 4 commits into
Open
keithadler wants to merge 4 commits into
keithadler wants to merge 4 commits into
Conversation
Replace `aspectRatio(contentMode:)` with `scaledToFit()`/`scaledToFill()` (legacy_swiftui_aspect_ratio) and drop two `swiftlint:disable` comments that no longer suppress anything, since `URL(string:)!` with a literal is no longer flagged by `force_unwrapping`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Switch the target to the Swift 6 language mode (strict concurrency checking) and enable the MemberImportVisibility upcoming feature, matching the settings Xcode 26 recommends. The project builds with zero warnings in both Debug and Release. Most changes make existing main-thread assumptions explicit rather than changing behavior: - Annotate main-thread-only helpers (`HotkeyRegistry`, `IceBarColorManager`, `MenuBarItemImageCache`, the color picker and window reader coordinators, `BindingExposable`, `SystemAppearance.current`, `NSStatusItem.showMenu`) with `@MainActor`. - Bridge C callbacks that Core Graphics and Carbon deliver on the main thread (event taps, run loop observers, hotkey handlers) into the main actor with `MainActor.assumeIsolated`, documenting why that is sound at each site. - Replace unsynchronized global mutable state with an instance property (`HotkeyRegistry.nextID`), a lock (`ScreenCapture.cachedCheckPermissions`) and an instance-owned `SystemWideElement` instead of AXSwift's global. - Keep the image capture in `MenuBarItemImageCache` off the main thread by moving it into a `nonisolated` static function that takes only `Sendable` inputs, so the cache's state is now mutated exclusively on the main actor. - Collect relaunch failures in `MenuBarItemSpacingManager` through task group results instead of a captured array. - `RunLoopLocalEventMonitor` now always observes the main run loop; it drives `NSApp.nextEvent` and `NSApp.postEvent`, which are only valid there. - Add the imports that MemberImportVisibility requires, and handle the `.extraLarge` control size in `CustomColorPicker`. Default main-actor isolation (`SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor`) was evaluated and deliberately not enabled: it would silently move the menu bar item image capture onto the main thread. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The version requirement already allowed it; this only moves the pin. `SPUUpdaterDelegate` is now annotated for concurrency upstream, so the `@preconcurrency` on the conformance is no longer needed (and warns). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The lint workflow used a third-party action bundling an old SwiftLint, which let violations that current releases report slip through. Run the official SwiftLint container image instead, pinned to 0.65.1. Add a build workflow that compiles the Release configuration with Xcode 26 on every push and pull request, and uploads the unsigned app as an artifact so changes can be tried without a local build. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Brings the project up to the current toolchain: Swift 6 language mode, Xcode 26 recommended settings, a current Sparkle, and CI that actually catches problems.
MemberImportVisibilityupcoming feature. Debug and Release both build with zero warnings.ghcr.io/realm/swiftlint:0.65.1image. The previous third-party action bundled an old SwiftLint, so these violations were never reported on PRs.macos-26for every push and PR, and uploads the unsigned app as an artifact so changes can be tried without a local build..extraLargecontrol size inCustomColorPicker, which was the only compiler warning before this change.How the concurrency migration was approached
Nearly all of the Swift 6 diagnostics were about main-thread-only code that had no isolation annotation. The changes make those assumptions explicit rather than changing where code runs:
@MainActoron main-thread-only helpers:HotkeyRegistry,IceBarColorManager,MenuBarItemImageCache, the color picker and window reader coordinators,BindingExposable,SystemAppearance.current,NSStatusItem.showMenu.MainActor.assumeIsolated. Each site has a comment explaining why that is sound.HotkeyRegistryuses an instance counter,ScreenCapture.cachedCheckPermissionsuses anOSAllocatedUnfairLock, andMenuBarManagerowns its ownSystemWideElementinstead of using AXSwift's globalvar.MenuBarItemImageCacheis now@MainActor, but the actual screen capture stays off the main thread: it moved into anonisolated staticfunction that takes onlySendableinputs. The cache'simages,screenandmenuBarHeightare now only mutated on the main actor (previouslyscreen/menuBarHeightwere written from a background task).MenuBarItemSpacingManager.applyOffsetcollects relaunch failures through task group results instead of a captured array.HotkeyRegistry.registertakes aKeyCombinationinstead of aHotkey; it only ever read the key combination.Two deliberate behavior notes:
RunLoopLocalEventMonitornow always observes the main run loop instead of the current one. It drivesNSApp.nextEvent/NSApp.postEvent, which are only valid on the main thread, so observing any other run loop could never have worked.SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor(Xcode 26's "approachable concurrency" default) was evaluated and not enabled. With it, the whole project compiles with only two errors, but it silently moves the menu bar item image capture onto the main thread. Explicit isolation keeps the existing threading and documents it.Test plan
xcodebuildDebug and Release: zero warnings, zero errors (Xcode 26.6)swiftlint --strictwith SwiftLint 0.65.1: cleanassumeIsolatedsites are exercised at startup by the event taps, hotkeys and image cache)Commits are split by concern (lint fixes, Swift 6, Sparkle, CI) so any one of them can be dropped independently.
🤖 Generated with Claude Code