Skip to content

fix: Swift 6 strict-concurrency build errors on newer Xcode toolchains#80

Open
iOSDevSK wants to merge 1 commit into
KartikLabhshetwar:mainfrom
iOSDevSK:fix/swift-6-concurrency-build-errors
Open

fix: Swift 6 strict-concurrency build errors on newer Xcode toolchains#80
iOSDevSK wants to merge 1 commit into
KartikLabhshetwar:mainfrom
iOSDevSK:fix/swift-6-concurrency-build-errors

Conversation

@iOSDevSK

Copy link
Copy Markdown

Problem

main currently fails to build with recent Xcode releases — stricter Swift 6 sendability checking turns several patterns into hard errors (12 errors across 8 files), e.g.:

AnnotationDrawing.swift:8: error: static property 'ciContext' is not concurrency-safe because non-'Sendable' type 'CIContext' may have shared mutable state
ScreenRecordingManager.swift:45: error: non-sendable result type 'SCShareableContent' cannot be sent from nonisolated context
MenuBarContentView.swift:130: error: sending 'screen' risks causing data races

Fix

Minimal, mechanical fixes with no behavior changes:

  • nonisolated(unsafe) for the shared CIContext statics in AnnotationDrawing and RedactionImageProcessor (CIContext is documented thread-safe)
  • @preconcurrency import for ScreenCaptureKit / AVFoundation where non-Sendable SDK results cross isolation boundaries
  • @Sendable download-delegate callbacks in AppUpdater
  • nonisolated HistoryStore URL/thumbnail accessors (called from detached thumbnail-loading tasks)
  • @MainActor on the text-view Coordinator in AnnotationItemView
  • Main-actor Task instead of Task.detached for menu bar actions (the 200 ms delayed capture/recording start doesn't need a background thread)

Testing

  • make release builds clean (Release, Xcode 26 toolchain)
  • Manually exercised capture, recording, editor export, history thumbnails, and updater — behavior unchanged

🤖 Generated with Claude Code

main does not compile with recent Xcode releases (stricter Swift 6
sendability checking). Minimal fixes, no behavior changes:

- nonisolated(unsafe) for the shared CIContext statics in
  AnnotationDrawing and RedactionImageProcessor
- @preconcurrency import ScreenCaptureKit / AVFoundation where
  non-Sendable SDK results cross isolation boundaries
- @sendable download-delegate callbacks in AppUpdater
- nonisolated HistoryStore URL/thumbnail accessors (called from
  detached thumbnail-loading tasks)
- @mainactor on the text-view Coordinator in AnnotationItemView
- main-actor Task instead of Task.detached for menu bar actions
  (sending 'screen' data-race errors)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 14, 2026

Copy link
Copy Markdown

Someone is attempting to deploy a commit to the knox projects Team on Vercel.

A member of the Team first needs to authorize it.

enum AnnotationDrawing {

private static let ciContext = CIContext(options: [.cacheIntermediates: false])
nonisolated(unsafe) private static let ciContext = CIContext(options: [.cacheIntermediates: false])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth adding a one-liner here so future readers know why the unsafe is intentional.

Suggested change
nonisolated(unsafe) private static let ciContext = CIContext(options: [.cacheIntermediates: false])
// CIContext is documented as thread-safe; this shared instance is safe to use across tasks.
nonisolated(unsafe) private static let ciContext = CIContext(options: [.cacheIntermediates: false])

return cache
}()
private static let ciContext = CIContext(options: [.cacheIntermediates: false])
nonisolated(unsafe) private static let ciContext = CIContext(options: [.cacheIntermediates: false])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same idea here: a short comment helps justify the unsafe and avoids it looking like an arbitrary suppression.

Suggested change
nonisolated(unsafe) private static let ciContext = CIContext(options: [.cacheIntermediates: false])
// CIContext is documented as thread-safe; this shared instance is safe to use across tasks.
nonisolated(unsafe) private static let ciContext = CIContext(options: [.cacheIntermediates: false])

}

func thumbnail(for record: CaptureRecord, maxSize: CGFloat = 120) -> NSImage? {
nonisolated func thumbnail(for record: CaptureRecord, maxSize: CGFloat = 120) -> NSImage? {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

urlForRecord / displayURLForRecord being nonisolated makes sense, but I’d be a bit cautious about making the thumbnail generation nonisolated since it constructs NSImage (AppKit).

If you don’t specifically need to do AppKit work off-main, keeping this @MainActor and doing the background work at the call site tends to be safer.

Suggested change
nonisolated func thumbnail(for record: CaptureRecord, maxSize: CGFloat = 120) -> NSImage? {
func thumbnail(for record: CaptureRecord, maxSize: CGFloat = 120) -> NSImage? {

let screen = originScreen
dismissPopover()
Task.detached {
Task {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, but making the actor hop explicit here helps keep sendability checking predictable if this action ever stops being main-actor-bound.

Suggested change
Task {
Task { @MainActor in

let onProgress: (Double) -> Void
var completion: ((Result<URL, Error>) -> Void)?
let onProgress: @Sendable (Double) -> Void
nonisolated(unsafe) var completion: (@Sendable (Result<URL, Error>) -> Void)?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is now nonisolated(unsafe), it’d help to document the threading assumption (set before resume(), invoked at most once from delegate callbacks). That makes it easier to audit for races later.

Suggested change
nonisolated(unsafe) var completion: (@Sendable (Result<URL, Error>) -> Void)?
// Set before `resume()` and invoked at most once from URLSession delegate callbacks.
nonisolated(unsafe) var completion: (@Sendable (Result<URL, Error>) -> Void)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant