fix(ios): add Swift 6 strict concurrency compatibility

Applies the same Swift 6 compatibility patterns from PR #166 (macOS) to the iOS app.

Changes:
- LocationService.swift: Added Sendable constraint to withTimeout<T> generic,
  made CLLocationManagerDelegate methods nonisolated with Task { @MainActor in }
  pattern to safely access MainActor state
- TalkModeManager.swift: Fixed OSLog string interpolation to avoid operator
  overload issues with OSLogMessage in Swift 6

Addresses #164

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Kristijan Jovanovski
2026-01-10 15:51:28 +01:00
committed by Peter Steinberger
parent d1943a9337
commit e4fea2b80b
2 changed files with 30 additions and 23 deletions

View File

@@ -86,9 +86,9 @@ final class LocationService: NSObject, CLLocationManagerDelegate {
}
}
private func withTimeout<T>(
private func withTimeout<T: Sendable>(
timeoutMs: Int,
operation: @escaping () async throws -> T) async throws -> T
operation: @escaping @Sendable () async throws -> T) async throws -> T
{
if timeoutMs == 0 {
return try await operation()
@@ -117,26 +117,35 @@ final class LocationService: NSObject, CLLocationManagerDelegate {
}
}
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
if let cont = self.authContinuation {
self.authContinuation = nil
cont.resume(returning: manager.authorizationStatus)
nonisolated func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
let status = manager.authorizationStatus
Task { @MainActor in
if let cont = self.authContinuation {
self.authContinuation = nil
cont.resume(returning: status)
}
}
}
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)
nonisolated func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let locs = locations
Task { @MainActor in
guard let cont = self.locationContinuation else { return }
self.locationContinuation = nil
if let latest = locs.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 err = error
Task { @MainActor in
guard let cont = self.locationContinuation else { return }
self.locationContinuation = nil
cont.resume(throwing: err)
}
}
}

View File

@@ -288,9 +288,8 @@ final class TalkModeManager: NSObject {
self.chatSubscribedSessionKeys.insert(key)
self.logger.info("chat.subscribe ok sessionKey=\(key, privacy: .public)")
} catch {
self.logger.warning(
"chat.subscribe failed sessionKey=\(key, privacy: .public) " +
"err=\(error.localizedDescription, privacy: .public)")
let err = error.localizedDescription
self.logger.warning("chat.subscribe failed key=\(key, privacy: .public) err=\(err, privacy: .public)")
}
}
@@ -528,9 +527,8 @@ final class TalkModeManager: NSObject {
self.lastPlaybackWasPCM = false
result = await self.mp3Player.play(stream: stream)
}
self.logger.info(
"elevenlabs stream finished=\(result.finished, privacy: .public) " +
"dur=\(Date().timeIntervalSince(started), privacy: .public)s")
let duration = Date().timeIntervalSince(started)
self.logger.info("elevenlabs stream finished=\(result.finished, privacy: .public) dur=\(duration, privacy: .public)s")
if !result.finished, let interruptedAt = result.interruptedAt {
self.lastInterruptedAtSeconds = interruptedAt
}