feat(macos): add Swift 6 strict concurrency compatibility

Prepares the macOS app for Swift 6 strict concurrency mode by:

1. Adding Sendable conformance to WizardNextResult, WizardStartResult,
   and WizardStatusResult in GatewayModels.swift

2. Adding AnyCodable bridging helpers in OnboardingWizard.swift to
   handle type conflicts between ClawdisProtocol and local module

3. Making CLLocationManagerDelegate methods nonisolated in:
   - MacNodeLocationService.swift
   - PermissionManager.swift (LocationPermissionRequester)

   Using Task { @MainActor in } pattern to safely access MainActor
   state from nonisolated protocol requirements.

These changes are forward-compatible and don't affect behavior on
current Swift versions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Tu Nombre Real
2026-01-04 03:13:31 +01:00
committed by Peter Steinberger
parent 72a9e58777
commit b978cc4e91
4 changed files with 53 additions and 28 deletions

View File

@@ -91,19 +91,26 @@ final class MacNodeLocationService: NSObject, CLLocationManagerDelegate {
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let cont = self.locationContinuation else { return }
self.locationContinuation = nil
if let latest = locations.last {
cont.resume(returning: latest)
} else {
cont.resume(throwing: Error.unavailable)
// MARK: - CLLocationManagerDelegate (nonisolated for Swift 6 compatibility)
nonisolated func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
Task { @MainActor in
guard let cont = self.locationContinuation else { return }
self.locationContinuation = nil
if let latest = locations.last {
cont.resume(returning: latest)
} else {
cont.resume(throwing: Error.unavailable)
}
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Swift.Error) {
guard let cont = self.locationContinuation else { return }
self.locationContinuation = nil
cont.resume(throwing: error)
nonisolated func locationManager(_ manager: CLLocationManager, didFailWithError error: Swift.Error) {
let errorCopy = error // Capture error for Sendable compliance
Task { @MainActor in
guard let cont = self.locationContinuation else { return }
self.locationContinuation = nil
cont.resume(throwing: errorCopy)
}
}
}

View File

@@ -6,6 +6,20 @@ import SwiftUI
private let onboardingWizardLogger = Logger(subsystem: "com.clawdis", category: "onboarding.wizard")
// MARK: - Swift 6 AnyCodable Bridging Helpers
// These helpers bridge between ClawdisProtocol.AnyCodable and the local module
// to avoid Swift 6 strict concurrency type conflicts.
private typealias ProtocolAnyCodable = ClawdisProtocol.AnyCodable
private func bridgeToProtocol(_ value: Any) -> ProtocolAnyCodable {
ProtocolAnyCodable(value)
}
private func bridgeDict(_ dict: [String: Any]) -> [String: ProtocolAnyCodable] {
dict.mapValues { ProtocolAnyCodable($0) }
}
@MainActor
@Observable
final class OnboardingWizardModel {
@@ -307,12 +321,12 @@ private struct WizardOptionItem: Identifiable {
}
private struct WizardOption {
let value: AnyCodable?
let value: ProtocolAnyCodable?
let label: String
let hint: String?
}
private func decodeWizardStep(_ raw: [String: AnyCodable]?) -> WizardStep? {
private func decodeWizardStep(_ raw: [String: ProtocolAnyCodable]?) -> WizardStep? {
guard let raw else { return nil }
do {
let data = try JSONEncoder().encode(raw)
@@ -323,7 +337,7 @@ private func decodeWizardStep(_ raw: [String: AnyCodable]?) -> WizardStep? {
}
}
private func parseWizardOptions(_ raw: [[String: AnyCodable]]?) -> [WizardOption] {
private func parseWizardOptions(_ raw: [[String: ProtocolAnyCodable]]?) -> [WizardOption] {
guard let raw else { return [] }
return raw.map { entry in
let value = entry["value"]
@@ -337,7 +351,7 @@ private func wizardStepType(_ step: WizardStep) -> String {
(step.type.value as? String) ?? ""
}
private func anyCodableString(_ value: AnyCodable?) -> String {
private func anyCodableString(_ value: ProtocolAnyCodable?) -> String {
switch value?.value {
case let string as String:
return string
@@ -352,11 +366,11 @@ private func anyCodableString(_ value: AnyCodable?) -> String {
}
}
private func anyCodableStringValue(_ value: AnyCodable?) -> String? {
private func anyCodableStringValue(_ value: ProtocolAnyCodable?) -> String? {
value?.value as? String
}
private func anyCodableBool(_ value: AnyCodable?) -> Bool {
private func anyCodableBool(_ value: ProtocolAnyCodable?) -> Bool {
switch value?.value {
case let bool as Bool:
return bool
@@ -367,18 +381,18 @@ private func anyCodableBool(_ value: AnyCodable?) -> Bool {
}
}
private func anyCodableArray(_ value: AnyCodable?) -> [AnyCodable] {
private func anyCodableArray(_ value: ProtocolAnyCodable?) -> [ProtocolAnyCodable] {
switch value?.value {
case let arr as [AnyCodable]:
case let arr as [ProtocolAnyCodable]:
return arr
case let arr as [Any]:
return arr.map { AnyCodable($0) }
return arr.map { ProtocolAnyCodable($0) }
default:
return []
}
}
private func anyCodableEqual(_ lhs: AnyCodable?, _ rhs: AnyCodable?) -> Bool {
private func anyCodableEqual(_ lhs: ProtocolAnyCodable?, _ rhs: ProtocolAnyCodable?) -> Bool {
switch (lhs?.value, rhs?.value) {
case let (l as String, r as String):
return l == r

View File

@@ -289,10 +289,14 @@ final class LocationPermissionRequester: NSObject, CLLocationManagerDelegate {
}
}
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
guard let cont = self.continuation else { return }
self.continuation = nil
cont.resume(returning: manager.authorizationStatus)
// nonisolated for Swift 6 strict concurrency compatibility
nonisolated func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
let status = manager.authorizationStatus
Task { @MainActor in
guard let cont = self.continuation else { return }
self.continuation = nil
cont.resume(returning: status)
}
}
}

View File

@@ -871,7 +871,7 @@ public struct WizardStep: Codable {
}
}
public struct WizardNextResult: Codable {
public struct WizardNextResult: Codable, Sendable {
public let done: Bool
public let step: [String: AnyCodable]?
public let status: AnyCodable?
@@ -896,7 +896,7 @@ public struct WizardNextResult: Codable {
}
}
public struct WizardStartResult: Codable {
public struct WizardStartResult: Codable, Sendable {
public let sessionid: String
public let done: Bool
public let step: [String: AnyCodable]?
@@ -925,7 +925,7 @@ public struct WizardStartResult: Codable {
}
}
public struct WizardStatusResult: Codable {
public struct WizardStatusResult: Codable, Sendable {
public let status: AnyCodable
public let error: String?