Skip to content

Add visionOS support#3

Draft
owenselles wants to merge 14 commits into
mainfrom
feature/visionos
Draft

Add visionOS support#3
owenselles wants to merge 14 commits into
mainfrom
feature/visionos

Conversation

@owenselles

Copy link
Copy Markdown
Owner

This pull request introduces significant enhancements for visionOS and tvOS support, refactors input handling for platform specificity, and improves the user experience across platforms. The changes include support for immersive streaming on visionOS, platform-specific input and audio session logic, and UI adaptations for both tvOS and visionOS.

Platform Support and Scene Management:

  • Added support for visionOS and tvOS platforms in the Xcode project, including new values for SUPPORTED_PLATFORMS and TARGETED_DEVICE_FAMILY, and disabled Mac Catalyst. [1] [2]
  • Introduced an ImmersiveStreamView and immersive scene management for visionOS, allowing full or mixed immersion streaming with user-selectable modes. The CloudNowApp now manages a shared GamesViewModel and immersion style state, and injects these into both the main window and immersive space. [1] [2] [3]

Platform-Specific Input and Audio Handling:

  • Refactored input handling to be platform-specific: Siri Remote handling and remote input mode toggling are now only compiled and used on tvOS, reducing unnecessary code on other platforms. [1] [2] [3] [4] [5] [6] [7] [8] [9]
  • Audio session configuration is now conditional: on tvOS, Bluetooth options are enabled for lowest latency; on visionOS, only the essential setup is performed. Microphone permission handling is also visionOS-aware. [1] [2]

UI and User Experience Improvements:

  • Button styles in library and store views now adapt to the platform: .card style for tvOS, .plain elsewhere, for a more native look and feel. [1] [2] [3] [4]
  • Added a visionOS-specific "Immersion Style" picker in the settings, letting users choose between full and mixed immersion, with real-time updates and explanatory text. [1] [2]

ViewModel and State Management:

  • The GamesViewModel now stores pending game/session info for visionOS immersive streaming, ensuring correct state transfer between scenes. MainTabView now uses the injected environment view model and centralizes play logic. [1] [2] [3]

Minor Fixes and Consistency:

  • Fixed favorite toggling to use game.id instead of the full game object for consistency. [1] [2]

These changes collectively improve platform support, streamline input and audio handling, and enhance the user experience on both visionOS and tvOS.

Add platform-specific support for tvOS and visionOS across project and runtime code.

- Xcode project: add SUPPORTED_PLATFORMS, disable Mac Catalyst, and include tvOS device family ("3,7").
- GFNStreamController: guard tvOS-only remoteMode and toggle; adjust AVAudioSession setup (Bluetooth options on tvOS, simplified handling for other platforms) and use visionOS for microphone permission request.
- InputSender: make RemoteInputMode, remote sensitivity, micro-gamepad handling, and related logic tvOS-only (Siri Remote). Ensure tick() processes micro gamepad only on tvOS and keep existing extended gamepad handling for other platforms.
- StreamView: gate onExitCommand to tvOS; show an explicit menu button on non-tvOS (visionOS) and show remote-mode toggle only on tvOS.
- VideoSurfaceView: use layerClass override on tvOS but add AVSampleBufferDisplayLayer as a sublayer on visionOS (and update layoutSubviews) because layerClass is not supported by the visionOS compositor.

These changes separate tvOS-specific Siri Remote behavior and audio routing from visionOS rendering and permission differences to ensure correct runtime behavior on both platforms.
Pass game.id to viewModel.toggleFavorite instead of the whole game object and guard .buttonStyle(.card) with #if os(tvOS) so the card button style is only applied on tvOS. Changes applied to CloudNow/UI/LibraryView.swift and CloudNow/UI/StoreView.swift to fix argument type and platform-specific styling.
Introduce an ImmersiveSpace-based streaming flow for visionOS: add ImmersiveStreamView, register an ImmersiveSpace scene in CloudNowApp, and wire up open/dismiss behavior from MainTabView. Persist and expose an immersion style via AppStorage (gfn.immersionStyle) with runtime syncing and a Settings picker to choose between full and mixed immersion; users can also toggle via the Digital Crown. Add pendingGame/pendingSession fields to GamesViewModel to pass the selected game/session into the ImmersiveSpace and clear state on dismiss. Also make small UI adjustments: use .plain buttonStyle on non-tvOS card buttons and update the exit button to borderedProminent with white foreground for better visibility.
Move GamesViewModel ownership to the App so a single instance can be injected into WindowGroup and ImmersiveSpace (visionOS) and shared across platforms. MainTabView now reads GamesViewModel from the environment instead of owning its own state. Refactor play flow into a private play(_:, session:) helper that sets sessionToResume and gameToPlay, update callers, and simplify active-session lookup. Also wire .environment(viewModel) at the App level and keep existing load/refresh/save tasks intact.
@owenselles owenselles self-assigned this Apr 18, 2026
@owenselles owenselles linked an issue Apr 18, 2026 that may be closed by this pull request
@owenselles owenselles added the enhancement New feature or request label Apr 18, 2026
@owenselles owenselles marked this pull request as ready for review April 21, 2026 23:03
Add platform-specific support for tvOS and visionOS across project and runtime code.

- Xcode project: add SUPPORTED_PLATFORMS, disable Mac Catalyst, and include tvOS device family ("3,7").
- GFNStreamController: guard tvOS-only remoteMode and toggle; adjust AVAudioSession setup (Bluetooth options on tvOS, simplified handling for other platforms) and use visionOS for microphone permission request.
- InputSender: make RemoteInputMode, remote sensitivity, micro-gamepad handling, and related logic tvOS-only (Siri Remote). Ensure tick() processes micro gamepad only on tvOS and keep existing extended gamepad handling for other platforms.
- StreamView: gate onExitCommand to tvOS; show an explicit menu button on non-tvOS (visionOS) and show remote-mode toggle only on tvOS.
- VideoSurfaceView: use layerClass override on tvOS but add AVSampleBufferDisplayLayer as a sublayer on visionOS (and update layoutSubviews) because layerClass is not supported by the visionOS compositor.

These changes separate tvOS-specific Siri Remote behavior and audio routing from visionOS rendering and permission differences to ensure correct runtime behavior on both platforms.
Pass game.id to viewModel.toggleFavorite instead of the whole game object and guard .buttonStyle(.card) with #if os(tvOS) so the card button style is only applied on tvOS. Changes applied to CloudNow/UI/LibraryView.swift and CloudNow/UI/StoreView.swift to fix argument type and platform-specific styling.
Introduce an ImmersiveSpace-based streaming flow for visionOS: add ImmersiveStreamView, register an ImmersiveSpace scene in CloudNowApp, and wire up open/dismiss behavior from MainTabView. Persist and expose an immersion style via AppStorage (gfn.immersionStyle) with runtime syncing and a Settings picker to choose between full and mixed immersion; users can also toggle via the Digital Crown. Add pendingGame/pendingSession fields to GamesViewModel to pass the selected game/session into the ImmersiveSpace and clear state on dismiss. Also make small UI adjustments: use .plain buttonStyle on non-tvOS card buttons and update the exit button to borderedProminent with white foreground for better visibility.
Move GamesViewModel ownership to the App so a single instance can be injected into WindowGroup and ImmersiveSpace (visionOS) and shared across platforms. MainTabView now reads GamesViewModel from the environment instead of owning its own state. Refactor play flow into a private play(_:, session:) helper that sets sessionToResume and gameToPlay, update callers, and simplify active-session lookup. Also wire .environment(viewModel) at the App level and keep existing load/refresh/save tasks intact.
Wrap tvOS-specific input and UI behavior with #if os(tvOS) to ensure remoteMode, input pausing, focusSection, and gamepad UI only apply on tvOS. Expose RemoteInputMode enum on all platforms (removed the tvOS-only guard). Clamp saved FPS to UIScreen.main.maximumFramesPerSecond on tvOS and use a 120 Hz fallback for available FPS on non-tvOS. Remove duplicated visionOS pending session/game properties. Fix VideoSurfaceView memory access by caching plane pointers (srcY/srcU/srcV) before copying into CVPixelBuffer to avoid optional unwraps inside loops. Files changed: GFNStreamController.swift, InputSender.swift, GamesViewModel.swift, HomeView.swift, StreamView.swift, VideoSurfaceView.swift.
@owenselles owenselles closed this Jun 21, 2026
@owenselles owenselles reopened this Jul 10, 2026
Removes a copy-paste duplicate #if os(visionOS) Section("Display") block
in SettingsView that would have rendered two identical Immersion Style
pickers on visionOS. Also applies swiftformat to all modified files.
@owenselles owenselles marked this pull request as draft July 10, 2026 09:58
Resolves conflicts between the visionOS platform support branch and 21
commits on main (rumble/haptics, localization, InputSender refactor, HDR
fixes, save-in-game-settings, store filter chips, library refresh, etc.).

Merge strategy per file:
- InputSender.swift: took main's complete rewrite (DispatchSource timer,
  packet pooling, haptics, steam overlay); cross-platform without tvOS guards
- GFNStreamController.swift: took main's configureAudioSession() helper and
  sender.configure() API
- LibraryView.swift: took main's structure (refresh button, store filters,
  isLibraryLoading) + kept visionOS branch's #if os(tvOS) buttonStyle guard
- MainTabView.swift: merged visionOS env vars + main's selectedTab,
  tab values, and ControllerTabNavigationBridge
- SettingsView.swift: merged visionOS Display (immersion style) section
  before main's Controller (rumble) section
- StoreView.swift: kept main's L10n strings + visionOS branch's tvOS
  buttonStyle conditional
- StreamView.swift: merged .onPlayPauseCommand inside os(tvOS) block;
  used L10n for remoteModeLabel
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add visionOS support

1 participant