What is a tvOS Top Shelf extension, how do static and dynamic content differ, and what does tvOS not have compared with iOS?
Predict first
A tvOS app wants its “Continue Watching” row on the Home Screen. Which mechanism provides it?
// A: a Share extension
// B: a Top Shelf extension returning sectioned content with deep links, reading from the shared container
// C: a widget
- A
- B
- C
Mental model. The Top Shelf is a shop-window display the TV puts in front of your shop’s sign. The display is set by a courier who works from a locker you left behind (the shared container), not from inside the shop, and it has to be dressed for people looking from the other side of the room.
On the Apple TV Home Screen, when the user focuses an app in the top row, the area above shows that app’s Top Shelf: a banner or a row of content that works as an advertisement and a shortcut straight into a title. It is provided by a Top Shelf extension (com.apple.tv-top-shelf, using the TVServices framework, TVTopShelfContentProvider) that runs in its own process, not inside your tvOS app, and is started by the system, not by you. It returns TVTopShelfContent, in one of two styles. Static (TVTopShelfImageContent via the Top Shelf Image asset in the asset catalogue, with a Top Shelf Image Wide variant): a single large image that ships with the app, needs no extension code and updates only with the app. Dynamic: your provider returns TVTopShelfCarouselContent (a rotating full-width banner of large items with images and optional video) or TVTopShelfSectionedContent (rows of items, such as “Continue Watching” and “New”), each TVTopShelfCarouselItem or TVTopShelfSectionedItem with a title, an image URL or asset and a display action and a play action as deep links (TVTopShelfAction(url:)) that the system sends to your app, which must route them (a deep link to a title, or straight to playback). The system also asks the provider for content periodically and on certain events, caches it, and you can ask it to refresh with TVTopShelfContentProvider.topShelfContentDidChange() when the data changes (new episode, progress).
What makes it hard. Budget and timing: like all extensions it has little memory and a short time to produce content, and it can run when your app is not running, so it must read from a shared App Group container or fetch quickly itself (loadTopShelfContent(completionHandler:) is asynchronous). Image size: the images are shown across a room at 10-foot size, so provide correctly sized assets (for example 1920×720 for a carousel, with the safe area and no text near the edges) and let the system load them, but do not exceed memory by decoding giant images. User versus public content: some Top Shelf content is personal (continue watching) and requires knowing the signed-in user (a shared keychain or the container), and tvOS supports multiple users per Apple TV with user-specific Top Shelf and the TV provider / video subscriber account flows, so personal items must follow the current profile. Deep links must be validated, because they arrive from the system. What tvOS does not have: no share sheet or Share and Action extensions, no notification service extension and no push-modified notifications in the iOS sense (limited notifications), no widgets and no Live Activities, no App Clips, no background execution beyond a few specific modes, no browser, no local file access for users, and limited persistent storage (the system can purge it, so keep the server or iCloud as the source of truth). What it uses instead: Top Shelf, the focus engine and the Siri Remote, Apple TV user profiles, the TV app’s Up Next and the Video Subscriber Account and TV provider frameworks for single sign-on, and App Intents and shortcuts for some actions. Test on a real Apple TV or the simulator with a content provider fixture, and verify the deep links from every item.
tvOS has no share sheet or widgets; the Top Shelf extension supplies carousel or sectioned content that the system shows above the app.
Common traps
- Making the Top Shelf extension depend on the app having run. It runs independently; read from the shared container or fetch quickly.
- Forgetting multiple user profiles. Personal content must follow the current user, not whoever signed in last.
They might follow up with
- Static versus dynamic Top Shelf? Static is an image shipped with the app; dynamic is content the extension provides at run time, as a carousel or sections.
- What does tvOS lack that iOS has? Share extensions, notification service extensions, widgets, Live Activities and App Clips.
Related questions
- 19.1.1 What is an app extension, how is it launched, and what limits do extensions run under?
- 19.1.2 How do app groups, shared containers and keychain access groups let an app and its extensions share data, and…
- 17.6.2 What are App Clips and CarPlay, what constraints do they impose, and when would you build them?
How do you treat extensions, widgets and intents as a public API of… →