RORK LABJP
MAX — Rork Max generates native Swift apps for iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and iMessageNATIVE — It unlocks native capabilities React Native cannot reach: AR/LiDAR, Metal 3D, widgets, Dynamic Island, Live Activities, Siri Intents, and HealthKitRN — Standard Rork builds cross-platform apps with React Native (Expo), a good fit when you want something working fastCHOICE — Pick React Native for speed, or Rork Max when you need Apple hardware and OS integrationPRICE — Rork is free to start with paid plans from $25/mo; Rork Max is $200/moFLOW — Describe the app you want in plain language and Rork produces working code you can ship to the storesMAX — Rork Max generates native Swift apps for iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and iMessageNATIVE — It unlocks native capabilities React Native cannot reach: AR/LiDAR, Metal 3D, widgets, Dynamic Island, Live Activities, Siri Intents, and HealthKitRN — Standard Rork builds cross-platform apps with React Native (Expo), a good fit when you want something working fastCHOICE — Pick React Native for speed, or Rork Max when you need Apple hardware and OS integrationPRICE — Rork is free to start with paid plans from $25/mo; Rork Max is $200/moFLOW — Describe the app you want in plain language and Rork produces working code you can ship to the stores
Articles/App Dev
App Dev/2026-06-18Advanced

Building an Ambient Display App for Apple TV with Rork Max

Use the native Swift that Rork Max generates as a foundation for an Apple TV app that quietly plays on a loop. We cover Top Shelf, the focus engine, and seamless video loops, with the practical lessons that only surface in real operation.

Rork Max172tvOSApple TVSwift28AVPlayer

Premium Article

The thought came to me one evening: what if the artwork from my own wallpaper app simply played on the living-room TV? I had been running iPhone wallpaper apps as an indie developer for a few years, and I suddenly wanted to see them on a large screen. Apple TV tends to fall outside the usual no-code or cross-platform conversation, but once Rork Max started generating native Swift, it finally became a realistic option.

tvOS works quite differently from iPhone. There is no touch; instead of a finger, you operate through a concept called focus. It is also assumed to stay on for long stretches, so power draw and burn-in deserve attention. Here I will use a quiet "ambient display app" that just keeps playing video as a vehicle for walking through how to refine the Swift that Rork Max generates.

Why build Apple TV with Rork Max

Standard Rork builds cross-platform with React Native (Expo), but the heart of tvOS — the focus engine, Top Shelf, and low-level AVPlayer control — does not come naturally without native Swift. Rork Max generates native Swift for iPhone, iPad, Apple Watch, Apple TV, and Vision Pro, so when you are targeting Apple TV, this is the foundation.

My own rule of thumb is simple. When I want to ship iOS and Android together quickly, I use the React Native version; when I want to reach into Apple hardware or OS integration, I use Rork Max. Apple TV is a textbook case of the latter, because the cross-platform advantage simply does not exist here.

AspectReact Native RorkRork Max (native Swift)
tvOS supportEffectively unsupportedOfficially supported
Focus engineThin abstraction, awkwardNative in SwiftUI
Top Shelf extensionDifficultImplementable as an App Extension
Low-level video controlLimitedDirect AVFoundation access

If you ask Rork Max to "make an ambient app for Apple TV that plays video full-screen on a loop," it returns a SwiftUI scaffold. As-is, though, rough edges remain: it plays, but flashes black at the seam, or focus moves in ways you did not intend. Everything from here is the hand-work.

Building a seamless video loop

The first thing you hit is the loop seam. A naive implementation that rewinds the same clip with AVPlayer and seek(to: .zero) inserts a faint black frame on every rewind. On a large TV, that instant is clearly noticeable.

The fix is the combination of AVQueuePlayer and AVPlayerLooper. Internally it prefetches the next item and stitches playback without a gap.

import AVFoundation
import SwiftUI
 
final class AmbientPlayerModel: ObservableObject {
    let player: AVQueuePlayer
    private var looper: AVPlayerLooper?
 
    init(resourceName: String) {
        guard let url = Bundle.main.url(forResource: resourceName, withExtension: "mp4") else {
            self.player = AVQueuePlayer()
            return
        }
        let item = AVPlayerItem(url: url)
        let queue = AVQueuePlayer()
        // AVPlayerLooper handles the seamless loop
        self.looper = AVPlayerLooper(player: queue, templateItem: item)
        self.player = queue
        queue.isMuted = true
        queue.actionAtItemEnd = .advance
    }
 
    func start() { player.play() }
    func stop() { player.pause() }
}

The key is to keep AVPlayerLooper retained as a property. Make it a local variable and it gets released, and the loop stops. Rork Max's initial output often leaves this as a local variable, so I promote it to a property every time.

On the SwiftUI side, wrapping AVPlayerLayer thinly is easier to control than using VideoPlayer. VideoPlayer carries playback control UI that just gets in the way for an ambient use case.

struct PlayerLayerView: UIViewRepresentable {
    let player: AVQueuePlayer
 
    func makeUIView(context: Context) -> PlayerUIView {
        let view = PlayerUIView()
        view.playerLayer.player = player
        view.playerLayer.videoGravity = .resizeAspectFill
        return view
    }
    func updateUIView(_ uiView: PlayerUIView, context: Context) {}
}
 
final class PlayerUIView: UIView {
    override class var layerClass: AnyClass { AVPlayerLayer.self }
    var playerLayer: AVPlayerLayer { layer as! AVPlayerLayer }
}

Setting videoGravity to .resizeAspectFill fills the TV's aspect ratio without margins. My wallpaper assets have all sorts of aspect ratios, so switching this to .resizeAspect produced black bars on the sides and broke the ambient immersion.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Concrete code for seamless video loops using AVQueuePlayer and AVPlayerLooper
A Top Shelf extension that puts your work on the top row, starting the experience before launch
Practical criteria for balancing the tvOS focus engine with power use and always-on display
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

App Dev2026-06-16
Adding an Apple Wallet Stamp Card to a Rork Max App — Signing and Updates
You want a shop stamp card in Apple Wallet. When you issue a PassKit pass from a Swift app generated by Rork Max, the hard parts are not design — they are signing and remote updates. Here are the implementation essentials.
App Dev2026-06-16
Staging Wallpaper Packs Before the First Launch: Where Rork Max and Background Assets Fit
Content-heavy apps tend to greet new users with an empty grid. Background Assets downloads content out-of-band, ahead of the first launch. Here is how I implement it in Rork Max's native Swift, a domain Rork (Expo) cannot reach easily, plus how I decide when it is worth it.
App Dev2026-06-16
Building an iMessage Extension with Rork Max to Bring Your App's World into Messages — Notes on Distribution as Code
A walkthrough of adding an iMessage extension to the native Swift project Rork Max generates, sharing your app's assets right inside Messages. Covers compact/expanded presentation, sending messages, and diagnosing why the extension won't appear in the drawer — from an indie developer's distribution lens.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →