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:
committed by
Peter Steinberger
parent
72a9e58777
commit
b978cc4e91
@@ -91,19 +91,26 @@ final class MacNodeLocationService: NSObject, CLLocationManagerDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
|
// MARK: - CLLocationManagerDelegate (nonisolated for Swift 6 compatibility)
|
||||||
guard let cont = self.locationContinuation else { return }
|
|
||||||
self.locationContinuation = nil
|
nonisolated func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
|
||||||
if let latest = locations.last {
|
Task { @MainActor in
|
||||||
cont.resume(returning: latest)
|
guard let cont = self.locationContinuation else { return }
|
||||||
} else {
|
self.locationContinuation = nil
|
||||||
cont.resume(throwing: Error.unavailable)
|
if let latest = locations.last {
|
||||||
|
cont.resume(returning: latest)
|
||||||
|
} else {
|
||||||
|
cont.resume(throwing: Error.unavailable)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func locationManager(_ manager: CLLocationManager, didFailWithError error: Swift.Error) {
|
nonisolated func locationManager(_ manager: CLLocationManager, didFailWithError error: Swift.Error) {
|
||||||
guard let cont = self.locationContinuation else { return }
|
let errorCopy = error // Capture error for Sendable compliance
|
||||||
self.locationContinuation = nil
|
Task { @MainActor in
|
||||||
cont.resume(throwing: error)
|
guard let cont = self.locationContinuation else { return }
|
||||||
|
self.locationContinuation = nil
|
||||||
|
cont.resume(throwing: errorCopy)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,20 @@ import SwiftUI
|
|||||||
|
|
||||||
private let onboardingWizardLogger = Logger(subsystem: "com.clawdis", category: "onboarding.wizard")
|
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
|
@MainActor
|
||||||
@Observable
|
@Observable
|
||||||
final class OnboardingWizardModel {
|
final class OnboardingWizardModel {
|
||||||
@@ -307,12 +321,12 @@ private struct WizardOptionItem: Identifiable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private struct WizardOption {
|
private struct WizardOption {
|
||||||
let value: AnyCodable?
|
let value: ProtocolAnyCodable?
|
||||||
let label: String
|
let label: String
|
||||||
let hint: String?
|
let hint: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
private func decodeWizardStep(_ raw: [String: AnyCodable]?) -> WizardStep? {
|
private func decodeWizardStep(_ raw: [String: ProtocolAnyCodable]?) -> WizardStep? {
|
||||||
guard let raw else { return nil }
|
guard let raw else { return nil }
|
||||||
do {
|
do {
|
||||||
let data = try JSONEncoder().encode(raw)
|
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 [] }
|
guard let raw else { return [] }
|
||||||
return raw.map { entry in
|
return raw.map { entry in
|
||||||
let value = entry["value"]
|
let value = entry["value"]
|
||||||
@@ -337,7 +351,7 @@ private func wizardStepType(_ step: WizardStep) -> String {
|
|||||||
(step.type.value as? String) ?? ""
|
(step.type.value as? String) ?? ""
|
||||||
}
|
}
|
||||||
|
|
||||||
private func anyCodableString(_ value: AnyCodable?) -> String {
|
private func anyCodableString(_ value: ProtocolAnyCodable?) -> String {
|
||||||
switch value?.value {
|
switch value?.value {
|
||||||
case let string as String:
|
case let string as String:
|
||||||
return 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
|
value?.value as? String
|
||||||
}
|
}
|
||||||
|
|
||||||
private func anyCodableBool(_ value: AnyCodable?) -> Bool {
|
private func anyCodableBool(_ value: ProtocolAnyCodable?) -> Bool {
|
||||||
switch value?.value {
|
switch value?.value {
|
||||||
case let bool as Bool:
|
case let bool as Bool:
|
||||||
return 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 {
|
switch value?.value {
|
||||||
case let arr as [AnyCodable]:
|
case let arr as [ProtocolAnyCodable]:
|
||||||
return arr
|
return arr
|
||||||
case let arr as [Any]:
|
case let arr as [Any]:
|
||||||
return arr.map { AnyCodable($0) }
|
return arr.map { ProtocolAnyCodable($0) }
|
||||||
default:
|
default:
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func anyCodableEqual(_ lhs: AnyCodable?, _ rhs: AnyCodable?) -> Bool {
|
private func anyCodableEqual(_ lhs: ProtocolAnyCodable?, _ rhs: ProtocolAnyCodable?) -> Bool {
|
||||||
switch (lhs?.value, rhs?.value) {
|
switch (lhs?.value, rhs?.value) {
|
||||||
case let (l as String, r as String):
|
case let (l as String, r as String):
|
||||||
return l == r
|
return l == r
|
||||||
|
|||||||
@@ -289,10 +289,14 @@ final class LocationPermissionRequester: NSObject, CLLocationManagerDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
|
// nonisolated for Swift 6 strict concurrency compatibility
|
||||||
guard let cont = self.continuation else { return }
|
nonisolated func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
|
||||||
self.continuation = nil
|
let status = manager.authorizationStatus
|
||||||
cont.resume(returning: manager.authorizationStatus)
|
Task { @MainActor in
|
||||||
|
guard let cont = self.continuation else { return }
|
||||||
|
self.continuation = nil
|
||||||
|
cont.resume(returning: status)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -871,7 +871,7 @@ public struct WizardStep: Codable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct WizardNextResult: Codable {
|
public struct WizardNextResult: Codable, Sendable {
|
||||||
public let done: Bool
|
public let done: Bool
|
||||||
public let step: [String: AnyCodable]?
|
public let step: [String: AnyCodable]?
|
||||||
public let status: 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 sessionid: String
|
||||||
public let done: Bool
|
public let done: Bool
|
||||||
public let step: [String: AnyCodable]?
|
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 status: AnyCodable
|
||||||
public let error: String?
|
public let error: String?
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user