How do you build a production tvOS media app: AVPlayerViewController, Now Playing, performance on older boxes, limited background execution, and testing with the remote?
Predict first
A tvOS app wants to refresh its catalogue while closed so it opens with fresh data. What is realistic?
// A: a background fetch every 15 minutes, like on iPhone
// B: fetch on launch, show cached data first, and keep the server as the source of truth
// C: keep the app running forever
- A
- B
- C
Mental model. A tvOS media app is a cinema lobby and a projector. The lobby (browsing) must be navigable with one arrow-key, the projector (AVKit) should be the house’s own, not a homemade one, and the staff go home the moment the film ends (no background work), so everything needed must be ready when the doors open.
tvOS apps are media-first: most of the work is browsing a catalogue and playing video well. Use AVPlayerViewController for playback (it provides the standard transport controls, scrubbing with thumbnails, subtitle and audio selection, the info panel, Siri and remote integration, Picture in Picture on supported boxes, and the system’s Up Next and “Skip” affordances), and customise through its APIs instead of building your own: transportBarCustomMenuItems for extra actions, customInfoViewControllers for tabs in the info panel, contextualActions (for example “Skip Intro”), navigationMarkerGroups for chapters, and AVPlayerItem.interstitialTimeRanges or AVPlayerInterstitialEvent for ads and bumpers. Match the display: set AVDisplayCriteria (or let AVKit do it) so a 24 fps HDR film switches the TV to the right frame rate and dynamic range, supply HLS with several resolutions and audio variants (Dolby), and publish Now Playing metadata (MPNowPlayingInfoCenter) so the system shows what plays and the remote’s Play/Pause works through MPRemoteCommandCenter. Feed the system’s TV app with Up Next and “Continue watching” data (the TV provider and Up Next APIs) and implement deep links from the Top Shelf. Browsing: shelves of posters are collection views with focus-aware layouts (TVUIKit or SwiftUI LazyHStack in a scroll view), image loading must be downsampled and prefetched (poster images at 10-foot size are large, and memory is limited on the older hardware), and scrolling must stay at 60 fps.
Background and limits. There is no general-purpose background fetch on tvOS: apps run while in the foreground, and the system suspends them soon after they leave it; background audio and video playback continues under the media background modes, a background URLSession can complete downloads, and push notifications for background refresh are limited, so data is fetched when the app opens (and stale content shown first), with the server as the source of truth. Storage is small and purgeable (Caches is cleared at the system’s discretion, and there is no persistent file area users manage), so treat local data as a cache and use iCloud (NSUbiquitousKeyValueStore, CloudKit) for small cross-device state. Performance varies widely across Apple TV HD, 4K and newer models: profile on the oldest supported box, keep memory low (images and the player share limited RAM; the system kills apps that exceed it, silently), avoid heavy blur and full-screen transparency, and watch thermal throttling in a fanless box during long sessions. Testing: XCUITest drives tvOS with XCUIRemote.shared.press(.right), .select, .menu and .playPause, then asserts on element.hasFocus to check navigation order; add tests for the focus path of every screen, deep links, playback start and error states, and run them on the simulator for speed and on hardware for performance and HDR behaviour. Keep an eye on App Review rules specific to tvOS (the remote must be able to operate everything, sign-in must work without typing a long password) and on accessibility (VoiceOver, large text, reduced motion), which the remote-driven interface depends on.
tvOS has no general background fetch, so apps refresh when opened and present cached content immediately.
Common traps
- Building a custom player. You lose the system transport, Siri, PiP and Up Next integrations; customise
AVPlayerViewControllerinstead. - Planning background refresh or large local storage. Neither is reliable on tvOS; fetch when launched and keep the server as the source of truth.
They might follow up with
- How do you test focus navigation? XCUITest with
XCUIRemote, assertinghasFocuson the expected element. - Why match the display criteria? To play at the content’s native frame rate and dynamic range without a wrong-mode switch.
Related questions
- 20.4.1 How does the tvOS focus engine work: focusable items, preferred focus, focus guides, and SwiftUI’s focusable…
- 20.4.2 What input and layout rules define a good tvOS interface: the Siri Remote, controllers, the 10-foot UI, and…
- 18.4.1 How does HLS adaptive streaming work, why do stalls happen, and what does FairPlay add?
- 18.4.2 How do Now Playing info, remote commands, Picture in Picture and AirPlay integrate a player with the system?
← What input and layout rules define a good tvOS interface: the Siri…