-
Notifications
You must be signed in to change notification settings - Fork 63
Description
Approachable Swift and MainActor isolation
Summary
Swift 6.2 introduces "Approachable Concurrency" — a set of language changes designed to make Swift's concurrency model significantly easier to adopt incrementally. This issue tracks the work to modernize PPPC Utility's concurrency story, taking advantage of these new defaults, particularly around MainActor isolation.
Current State
PPPC Utility is currently on Swift 5.0 language mode with no strict concurrency checking enabled. The project:
Has zero MainActor annotations despite having 3 NSViewController subclasses and several NSObject-based model types that are accessed from the UI
Uses DispatchQueue.main.async in 4 places to manually hop to the main thread for UI updates
Uses async/await in the networking layer (e.g. JamfProAPIClient, Networking, UploadManager)
Has one custom actor (NetworkAuthManager) for safe token management
Has no Sendable conformances
Relies heavily on @objc dynamic properties with KVO/Cocoa Bindings for UI data flow
What Changes in Swift 6.2
Proposed Work
This could be done incrementally:
Phase 1: Enable strict concurrency checking in warning mode
Update the Swift language version from 5.0 to 6.0 (or at minimum 5.10)
Set SWIFT_STRICT_CONCURRENCY = targeted (or complete) in the Xcode build settings to surface warnings without breaking the build
Audit the warnings to understand the scope of work
Phase 2: Add MainActor isolation to UI and model types
Annotate TCCProfileViewController, SaveViewController, and OpenViewController with MainActor
Annotate the shared Model class (and related types like Executable, Policy, AppleEventRule, SigningIdentity) with MainActor since they are accessed from the UI via KVO/bindings
Replace DispatchQueue.main.async calls with structured concurrency patterns (e.g. await MainActor.run { } or just rely on inherited isolation)
Phase 3: Add Sendable conformances
Mark value types (TCCProfile, TCCPolicy, etc.) as Sendable
Mark enums (AuthError, AuthenticationInfo, TCCProfileDisplayValue, etc.) as Sendable
Audit reference types for thread safety
Phase 4: Adopt Swift 6.2 defaults
Enable InferIsolationFromMembers (SE-0466) to let the compiler infer MainActor for types that need it, potentially removing many explicit annotations
Verify that nonisolated(nonsending) defaults (SE-0461) don't introduce regressions in the networking layer
Remove any now-redundant DispatchQueue.main.async calls that are unnecessary under the new isolation model
Update the project to Swift 6.2 language mode
Benefits
Compiler-enforced thread safety — The compiler will catch main-thread violations at build time instead of leaving them as runtime crashes
Reduced boilerplate — No more manual DispatchQueue.main.async calls; the compiler handles actor isolation automatically
Safer networking — Clear boundaries between main-actor-isolated UI code and background networking code
Future-proof — Positions the project for the Swift 6 strict concurrency world
Notes
The heavy use of @objc dynamic and Cocoa Bindings adds complexity, as these patterns interact with MainActor in specific ways. Some types may need @preconcurrency MainActor during the transition.
The existing NetworkAuthManager actor is already well-structured and shouldn't need significant changes.
The AppDelegate class is trivial and should gain MainActor automatically via the @NSApplicationMain attribute.
This might be better suited for after the conversion to Swift UI which would eliminate most of the cocoa bindings.