Sample CodeiOS, iPadOS, Mac Catalyst, tvOSReviewed 2026-07-21View on Apple Developer

Creating a seamless multiview playback experience

At a glance

Item Summary
Purpose Present several streams, coordinate playback, prioritize network use, and nominate one focus player for audio and external playback.
App architecture SwiftUI MVVM with a menu view model, a layout-level player coordinator, and one observable state object per AVPlayer.
Main patterns MVVM, observable state, composition root, coordinated collaborators, and a SwiftUI-to-AVKit adapter.
Project style Feature roles are separated into Models, ViewModels, Views, and Utilities/Extensions.

Project structure

MultiviewPlaybackSampleApp/
├── MultiviewPlaybackSampleAppApp.swift  # Composition root
├── Menu.json                            # Stream configuration
├── Models/
│   ├── Menu.swift                       # Codable menu values
│   └── PlayerState.swift                # One player and its state
├── ViewModels/
│   ├── MainViewModel.swift              # Menu/navigation state
│   └── LayoutGridViewModel.swift        # Multi-player coordination
├── Views/
│   ├── MainView.swift
│   ├── GridView.swift / LayoutView.swift
│   ├── LayoutGridView.swift / PlayerView.swift
│   └── PlayerViewController.swift       # AVKit bridge
└── Utilities/                           # Decode, logging, URL, and style helpers

Structure observations

  • Folder names express architectural role; feature-specific names connect views to their view models.
  • LayoutGridViewModel is the aggregate owner for a screenful of players, while PlayerState owns one player’s framework behavior.
  • JSON decoding and framework-value conversion live in small extensions rather than either view model.

Overall architecture

Reference code

MultiviewPlaybackSampleApp/MultiviewPlaybackSampleAppApp.swift:11 and MultiviewPlaybackSampleApp/Views/MainView.swift:31 — two-level composition

let mainViewModel = MainViewModel.createViewModelWithMenu
MainView(viewModel: mainViewModel)

// When a playback mode is selected:
layoutGridViewModel = LayoutGridViewModel(selectedItem: viewModel.menuItem)
viewModel.showGridView()

Interpretation

The app creates long-lived menu state, while MainView creates a fresh layout coordinator for each playback destination. That coordinator creates and manages the player collection. PlayerState translates layout-level intent into AVFoundation and AVRouting operations.

Ownership and state

Ownership evidence

MultiviewPlaybackSampleApp/ViewModels/LayoutGridViewModel.swift:53 — layout-owned players

func createAllPlayers() {
    resetValues()
    for (index, asset) in selectedItem.assets.enumerated() {
        let playerState = PlayerState(/* asset values */)
        playerState.connectToAVFCoordinationMedium(
            coordinationMedium: coordinationMedium
        )
        playersToShow.append(playerState)
    }
}
Owner Object or state Relationship Mutation authority
App root MainViewModel Creates once and injects MainViewModel and bound view actions
MainView Optional LayoutGridViewModel Creates as @State, resets on return MainView replaces; layout model mutates internals
LayoutGridViewModel Player array, grid, focus, fullscreen, coordination medium Creates and coordinates Layout model methods in observed practice
PlayerState AVPlayer, KVO tokens, focus/playback flags Lazy-creates and observes PlayerState methods; properties remain module-settable
AVFoundation Playback coordinator relation Connects each player to shared medium PlayerState.connect... and invalidate()

Class and protocol design

MultiviewPlaybackSampleApp/Models/Menu.swift:11 — protocol composition for data models

typealias DataModel = (Codable & Equatable & Sendable)

struct MenuItem: DataModel, Identifiable { /* immutable fields */ }
struct MenuAsset: DataModel, Identifiable, Hashable { /* ... */ }
Type Responsibility Depends on
MainViewModel Decode menu, derive grid limits, and drive navigation Menu values, SwiftUI navigation
LayoutGridViewModel Create/remove players and coordinate focus, grid, and fullscreen state PlayerState, AVPlaybackCoordinationMedium
PlayerState Own one player, route focus, network priority, observation, and cleanup AVFoundation, AVRouting
PlayerViewController Adapt AVPlayerViewController into SwiftUI AVKit, PlayerState
DataModel composition Require menu values to be codable, comparable, and sendable Standard library protocols

There is protocol composition for value models, but no app-defined playback protocol. The player collaborators are concrete.

Access control

Symbol Access Verified effect Likely rationale
PlayerState.queue private Inaccessible outside PlayerState. Reserves implementation-only scheduling state, although this source does not otherwise use the queue.
Menu fields internal immutable let Readable within the target but not replaceable. Configuration is value data loaded once.
View-model and player observable properties internal by default and mostly mutable Any code in the app target can mutate them. Keeps the sample concise, but ownership conventions are not compiler-enforced.
PlayerViewController.init declared public, effectively capped by internal enclosing type Still not an exported reusable API. Wider than needed; likely incidental sample style.
App types internal by default No architecture surface is exported outside the app module. Appropriate for an application target.

Reference code

MultiviewPlaybackSampleApp/Models/PlayerState.swift:17 — broad observable surface with one private detail

let assetURL: URL
var playerRate: Float = 0
var isFocusPlayer = false
var isFullScreen = false
private let queue = DispatchQueue(label: "com.apple...PlayerState.queue")

Logic ownership and placement

Logic Owning type or file Placement rationale
Menu loading and route selection MainViewModel Top-level feature state
Grid geometry, player creation, focus, and fullscreen LayoutGridViewModel Operations span the complete player collection
Playback, seeking, muting, route preference, KVO cleanup PlayerState Operations concern exactly one AVPlayer lifecycle
AVKit embedding PlayerViewController Platform-view adaptation kept out of domain state
Network-priority mapping NetworkPriority extension Translation stays beside the source enum

Design patterns

Pattern Source evidence Purpose or tradeoff
MVVM MultiviewPlaybackSampleApp/Views/MainView.swift:12, MultiviewPlaybackSampleApp/Views/LayoutGridView.swift:12 Views bind to observable coordinators instead of directly managing player graphs.
Aggregate coordinator MultiviewPlaybackSampleApp/ViewModels/LayoutGridViewModel.swift:54 One owner applies collection-wide focus and lifecycle rules.
Per-resource state object MultiviewPlaybackSampleApp/Models/PlayerState.swift:16 Encapsulates one player’s AVFoundation and AVRouting behavior.
Adapter MultiviewPlaybackSampleApp/Views/PlayerViewController.swift:13 Bridges UIKit/AVKit playback into SwiftUI.
Protocol composition MultiviewPlaybackSampleApp/Models/Menu.swift:11 Applies consistent data contracts without defining an empty marker protocol.

Naming conventions

  • Role suffixes are consistent: View, ViewModel, State, and ViewController.
  • Collection-level methods name their scope: createAllPlayers, setFocusOnPlayer, and setFullScreenPlayer.
  • Boolean properties use is/should: isFocusPlayer, isFullScreen, shouldBeHidden.
  • Extension files use Type+Additions.swift; view files match their primary type.

Architecture takeaways

  • Model a multi-player screen with one collection coordinator and one state owner per player.
  • Share a playback coordination medium while keeping player cleanup local.
  • A view adapter is an effective boundary between declarative SwiftUI and AVPlayerViewController.
  • If this grew beyond a sample, private(set) and playback protocols would enforce the ownership already implied by call sites.

Source map

Source file Relevant symbols
MultiviewPlaybackSampleApp/MultiviewPlaybackSampleAppApp.swift app composition root
MultiviewPlaybackSampleApp/ViewModels/MainViewModel.swift MainViewModel, MenuNavigationRoute
MultiviewPlaybackSampleApp/ViewModels/LayoutGridViewModel.swift LayoutGridViewModel
MultiviewPlaybackSampleApp/Models/PlayerState.swift PlayerState
MultiviewPlaybackSampleApp/Models/Menu.swift menu values, DataModel, NetworkPriority
MultiviewPlaybackSampleApp/Views/PlayerViewController.swift AVKit bridge