Skip to content

Media and utilities Swift 6#808

Open
stevenzeck wants to merge 4 commits into
readium:swift6from
stevenzeck:media-and-utilities
Open

Media and utilities Swift 6#808
stevenzeck wants to merge 4 commits into
readium:swift6from
stevenzeck:media-and-utilities

Conversation

@stevenzeck

@stevenzeck stevenzeck commented Jun 6, 2026

Copy link
Copy Markdown
Contributor
  • Removed Atomic.swift and the @Atomic property wrapper.
  • Isolated Navigator, Configurable, AudioSessionUser, PublicationSpeechSynthesizer, and NowPlayingInfo to @MainActor.
  • Implemented @unchecked Sendable wrappers (TimeObserverToken and NotificationObserverToken) for AVPlayer KVO and NotificationCenter.
  • Updated Cancellable protocol, CancellableObject, and MediatorCancellable to conform to Sendable, using Mutex to secure callbacks.
  • Created a dedicated Poller class for condition-polling, requiring @Sendable and @MainActor isolation for all callbacks.
  • Isolated UserPreferencesViewModel and TTSViewModel to @MainActor and utilized MainActor.assumeIsolated within Combine callbacks to resolve compilation errors in the TestApp.

Public API Breaking Changes

  • Navigator, Configurable, AudioSessionUser, PublicationSpeechSynthesizer, and NowPlayingInfo are now @MainActor isolated.
  • Atomic class and @Atomic property wrapper removed from ReadiumShared.
  • Cancellable, CancellableObject, and MediatorCancellable now conform to Sendable.
  • AudioSession.end(for:) parameter type changed from AudioSessionUser to ObjectIdentifier.
  • execute(when:pollingInterval:on::) parameters and return type updated to require @Sendable @MainActor.
  • CancellableObject.init(onCancel:) closure now requires @Sendable.
  • throttle(duration:on::) closure and return type now require @Sendable.

Relates to #758.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR advances Swift 6 / strict concurrency compliance across the media/toolkit utilities and navigator layers by tightening actor isolation (@MainActor) and updating utility primitives and callbacks to be Sendable-safe.

Changes:

  • Isolates key navigator/media APIs and related view models to @MainActor.
  • Reworks control-flow and cancellation utilities to require @Sendable callbacks and uses Mutex for thread-safe state.
  • Introduces a polling utility (execute(when:...) via a new Poller) and adds observer-token wrappers for AVPlayer/NotificationCenter observer lifetime management.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
TestApp/Sources/Reader/Common/TTS/TTSViewModel.swift Marks the view model as @MainActor to satisfy strict concurrency.
TestApp/Sources/Reader/Common/Preferences/UserPreferences.swift Marks preferences view model @MainActor and adjusts Combine callbacks for actor isolation.
Sources/Shared/Toolkit/Poller.swift Adds a dedicated polling implementation and updates execute(when:...) to require @Sendable @MainActor callbacks.
Sources/Shared/Toolkit/Media/NowPlayingInfo.swift Isolates Now Playing manager to @MainActor and adapts throttled updates.
Sources/Shared/Toolkit/Media/AudioSession.swift Moves audio session user protocol + session management onto @MainActor and adjusts APIs for Swift 6.
Sources/Shared/Toolkit/ControlFlow.swift Updates throttle to be @Sendable-friendly and thread-safe via Mutex.
Sources/Shared/Toolkit/Cancellable.swift Makes cancellation primitives Sendable and guards state/callbacks with Mutex.
Sources/Shared/Toolkit/Atomic.swift Removes the legacy Atomic wrapper from ReadiumShared.
Sources/Navigator/VisualNavigator.swift Marks VisualNavigator default implementations as @MainActor.
Sources/Navigator/TTS/PublicationSpeechSynthesizer.swift Isolates synthesizer to @MainActor and updates delegate interactions and teardown.
Sources/Navigator/Preferences/Configurable.swift Marks Configurable as @MainActor to align UI-facing configuration flows with isolation.
Sources/Navigator/Navigator.swift Marks Navigator (and convenience extension) as @MainActor.
Sources/Navigator/Audiobook/AudioNavigator.swift Isolates AudioNavigator to @MainActor, introduces observer token wrappers, and updates callback/sendability to satisfy Swift 6.
Comments suppressed due to low confidence (1)

Sources/Navigator/Audiobook/AudioNavigator.swift:375

  • completion is declared @MainActor, but it’s called from a DispatchQueue.main.async closure which is not guaranteed to be on the MainActor executor. This will typically trigger a Swift 6 actor-isolation error unless you explicitly assert/hop onto the MainActor before calling completion.
            DispatchQueue.main.async {
                completion(info)
            }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Sources/Shared/Toolkit/Poller.swift Outdated
Comment on lines +45 to +48
Task {
await block()
isPolling = false
}
Comment on lines +569 to +576
deinit {
// Technically, AVFoundation requires `removeTimeObserver` to be called
// from the same queue that added the observer (which was `.main`).
// However, since we cannot easily dispatch to the main actor during deinit
// without risking an async lifetime leak, we call it here.
// In practice, `AVPlayer.removeTimeObserver` handles this gracefully off-thread.
player.removeTimeObserver(observer)
}
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