Skip to content

Implement Swift 6 Approachable Concurrency migration (Stages 2–5)#144

Draft
Copilot wants to merge 8 commits intomain-actor-isolationfrom
copilot/create-separate-pull-requests
Draft

Implement Swift 6 Approachable Concurrency migration (Stages 2–5)#144
Copilot wants to merge 8 commits intomain-actor-isolationfrom
copilot/create-separate-pull-requests

Conversation

Copy link
Copy Markdown

Copilot AI commented Mar 26, 2026

Implements all pending stages from the plan at docs/plans/approachable-concurrency.md. Stage 1 (build settings) was already applied. Each stage is a separate commit.

Stage 2: NetworkAuthManager actor → class

With SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor, MainActor serialization replaces the actor. bearerAuthSupported() drops async, basicAuthString() drops nonisolated. Resolves Token.isValid cross-isolation warning.

Stage 3a: Remove DispatchQueue.main.async wrappers

Three locations (Alert.display, OpenViewController selection handler, SaveViewController.savePressed panel callback) — redundant under MainActor default isolation.

Stage 3b: UploadManager → async throws

// Before
func verifyConnection(authManager:completionHandler:)
func upload(profile:authMgr:siteInfo:signingIdentity:completionHandler:)

// After
func verifyConnection(authManager:) async throws -> VerificationInfo
func upload(profile:authMgr:siteInfo:signingIdentity:) async throws

Button action in UploadInfoView bridges with Task { await ... } — the one idiomatic place a Task{} remains.

Stage 3c: Model.loadExecutable → direct return

Completion handler removed since the work is synchronous. findExecutableOnComputerUsingfindExecutable, callers across TCCProfileViewController, OpenViewController updated.

Stage 3d: TCCProfileImporter → direct return

Both decodeTCCProfile overloads now throws -> TCCProfile. TCCProfileImportCompletion typealias removed. Tests updated from callback assertions to do/catch.

Stage 4: @concurrent for background I/O

  • SecurityWrapper.copyDesignatedRequirement, .sign, .loadSigningIdentities@concurrent static func ... async throws
  • Model.loadExecutable@concurrent func ... async throws -> Executable
  • Async cascades to getAppleEventChoices, getExecutableFrom, importProfile, getExecutablesFromAllPolicies and all callers

Stage 5: Swift 6 language mode

  • SWIFT_VERSION = 5.06.0 (4 build configurations)
  • SaveViewController.observeValue: nonisolated + MainActor.assumeIsolated for KVO override mismatch
  • ModelTests.setUp: same pattern for XCTestCase lifecycle mismatch

💬 Send tasks to Copilot coding agent from Slack and Teams to turn conversations into code. Copilot posts an update in your thread when it's finished.

Copilot AI and others added 7 commits March 26, 2026 20:33
With SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor, all types are MainActor
by default. The actor's synchronization purpose (single-flight token
refresh) is now provided by MainActor serialization.

Changes:
- actor NetworkAuthManager → class NetworkAuthManager
- bearerAuthSupported(): drop async (no longer needed without actor)
- basicAuthString(): drop nonisolated (no longer an actor)
- JamfProAPIClient: drop await from bearerAuthSupported() calls
- NetworkAuthManagerTests: drop await from bearerAuthSupported() calls

Side effect: Resolves Token.isValid cross-isolation warning because
Token and NetworkAuthManager are now both on MainActor.

Agent-Logs-Url: https://github.com/jamf/PPPC-Utility/sessions/4235dbf5-feb0-4744-839d-543eee3b8ee5

Co-authored-by: watkyn <40115+watkyn@users.noreply.github.com>
With MainActor default isolation, manual dispatch to the main thread
is redundant. All three locations are already MainActor-isolated.

Changes:
- Alert.swift: Remove DispatchQueue.main.async wrapper in display()
- OpenViewController.swift: Remove wrapper in tableView selectionIndexes
- SaveViewController.swift: Remove wrapper in savePressed panel callback

Agent-Logs-Url: https://github.com/jamf/PPPC-Utility/sessions/4235dbf5-feb0-4744-839d-543eee3b8ee5

Co-authored-by: watkyn <40115+watkyn@users.noreply.github.com>
Replace Task/completion handler pattern with direct async throws methods.

Changes:
- UploadManager.verifyConnection: async throws -> VerificationInfo
- UploadManager.upload: async throws (no completion handler)
- UploadInfoView.verifyConnection: now async, uses try await
- UploadInfoView.performUpload: now async, uses try await
- Button action bridges to async with Task { await ... }

Agent-Logs-Url: https://github.com/jamf/PPPC-Utility/sessions/4235dbf5-feb0-4744-839d-543eee3b8ee5

Co-authored-by: watkyn <40115+watkyn@users.noreply.github.com>
Replace completion handler pattern with throws -> Executable since the
work is synchronous (reads bundle info and code requirements).

Changes:
- Model.loadExecutable(url:) now throws -> Executable (no completion)
- Remove LoadExecutableCompletion typealias
- findExecutableOnComputerUsing → findExecutable, returns directly
- getExecutableFrom: uses do/catch instead of completion handler
- getAppleEventChoices: uses do/catch for each loadExecutable call
- TCCProfileViewController: promptForExecutables and acceptDrop updated
- OpenViewController.prompt: updated to use try/catch

Agent-Logs-Url: https://github.com/jamf/PPPC-Utility/sessions/4235dbf5-feb0-4744-839d-543eee3b8ee5

Co-authored-by: watkyn <40115+watkyn@users.noreply.github.com>
Replace completion handler pattern with throws -> TCCProfile since
the work is synchronous (decode data, read file).

Changes:
- TCCProfileImporter.decodeTCCProfile(data:) now throws -> TCCProfile
- TCCProfileImporter.decodeTCCProfile(fileUrl:) now throws -> TCCProfile
- Remove TCCProfileImportCompletion typealias
- TCCProfileConfigurationPanel: uses try/catch in panel callback
- TCCProfileImporterTests: updated to use try/catch pattern

Agent-Logs-Url: https://github.com/jamf/PPPC-Utility/sessions/4235dbf5-feb0-4744-839d-543eee3b8ee5

Co-authored-by: watkyn <40115+watkyn@users.noreply.github.com>
Mark SecurityWrapper and Model methods with @Concurrent to move disk
I/O and security operations off MainActor onto the cooperative pool.

Changes:
- SecurityWrapper.copyDesignatedRequirement: @Concurrent async throws
- SecurityWrapper.sign: @Concurrent async throws
- SecurityWrapper.loadSigningIdentities: @Concurrent async throws
- Model.loadExecutable: @Concurrent async throws -> Executable
- Model.getAppleEventChoices: now async (calls loadExecutable)
- Model.getExecutableFrom/findExecutable: now async
- Model.getExecutablesFromAllPolicies: now async
- Model.importProfile: now async
- All callers updated to use Task/await where needed
- ModelTests: updated to async test methods

Agent-Logs-Url: https://github.com/jamf/PPPC-Utility/sessions/4235dbf5-feb0-4744-839d-543eee3b8ee5

Co-authored-by: watkyn <40115+watkyn@users.noreply.github.com>
Flip SWIFT_VERSION from 5.0 to 6.0 so all concurrency warnings become
hard errors. Fix override isolation mismatches.

Changes:
- SWIFT_VERSION = 6.0 in all 4 build configurations
- SaveViewController.observeValue: add nonisolated + MainActor.assumeIsolated
- ModelTests.setUp: add nonisolated + MainActor.assumeIsolated
- Update plan document to reflect all stages complete

Agent-Logs-Url: https://github.com/jamf/PPPC-Utility/sessions/4235dbf5-feb0-4744-839d-543eee3b8ee5

Co-authored-by: watkyn <40115+watkyn@users.noreply.github.com>
Copilot AI changed the title [WIP] Add separate pull requests for each state in the plan Implement Swift 6 Approachable Concurrency migration (Stages 2–5) Mar 26, 2026
Copilot AI requested a review from watkyn March 26, 2026 20:45
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.

2 participants