Token导航 LogoToken导航

Agent Skill · 研究检索

axiom-tvos(公理电视)

axiom-tvos 用于查找、检索和筛选相关信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要根据关键词、任务场景或来源线索快速定位候选结果时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

axiom-tvos 是研究检索类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为需要联网;许可证信息还不明确,第三方扫描已通过,按安装量在研究检索类的 73,675 个 Skill 里排第 33,382。

总安装 2,373周安装 96GitHub Stars 873下载 745核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
unknown

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:axiom-tvos(公理电视)
来源仓库:https://github.com/charleswiltgen/axiom
仓库路径:skills/axiom-tvos
安装命令:
npx skills add https://github.com/charleswiltgen/axiom --skill axiom-tvos
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/charleswiltgen/axiom --skill axiom-tvos

这个 Skill 是做什么的

研究检索类 Skill 主要解决「找资料、核对事实、把来源整理清楚」的问题,通常会让助手先检索再总结。

axiom-tvos 指出 tvOS 与 iOS 的关键差异点,纠正“电视版即放大版手机应用”的错误认知。适用于端口移植前的风险评估,重点警示本地存储缺失、WebView 不可用与焦点系统不同三大陷阱。提出双焦点系统概念,强调遥控器导航与触控交互并存带来的 UX 设计挑战。

推荐采用独立设计语言而非简单拉伸布局,特别是针对 Apple TV 26 的 Liquid Glass 新风格适配。部署前务必在真实设备上测试焦点移动路径与语音控制集成效果。

来源仓库内路径:skills/axiom-tvos

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/charleswiltgen/axiom --skill axiom-tvos;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要需要联网,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

axiom-tvos 怎么安装?

在终端执行 npx skills add https://github.com/charleswiltgen/axiom --skill axiom-tvos。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

axiom-tvos 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

axiom-tvos 需要什么权限?

权限级别是需要联网。该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

axiom-tvos 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

axiom-tvos 用的人多吗?

公开数据是累计安装 2,373 次,近一周安装 96 次,下载 745 次,GitHub 873 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

axiom-tvos 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理研究检索类任务、又不想每次都手写提示词的人。

不太适合

  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

研究检索类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
find-skills

寻找技能 · Codex、Claude

31,199,81316,486操作浏览器MIT有提醒
self-improving-agent

自我改进 Agent · OpenClaw

16,464,0023,396接触敏感数据MIT-0通过
soultrace

灵魂追踪 · Codex、Claude

7,588,94412需要联网未明确有提醒
self-improving

自我完善 · OpenClaw

6,829,2841,046需要联网MIT-0通过
microsoft-foundry

微软代工厂 · Codex、Claude

6,614,144764调用外部服务未明确有提醒
axiom-tvos

公理电视 · 本页

2,373873需要联网未明确通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

tvOS Development

Overview

tvOS shares UIKit and SwiftUI with iOS but diverges in critical ways that catch every iOS developer. The three most dangerous assumptions: (1) local files persist, (2) WebView exists, (3) focus works like @FocusState.

Core principle tvOS is not "iOS on TV." It has a dual focus system, no persistent local storage, no WebView, and a remote with two incompatible generations. Treat it as its own platform.

tvOS 26 Adopts Liquid Glass design language with new app icon system. See axiom-liquid-glass for implementation patterns.

tvOS Porting Triage

Before shipping a tvOS port, verify these five areas — they account for 90% of tvOS-specific bugs:

AreaCheckSection
StorageNo persistent local files — iCloud required§3
FocusDual system working, focus guides for gaps§1
WebViewReplaced with JavaScriptCore or native rendering§4
Text inputShadow input or fullscreen keyboard handled§6
AVPlayerAudio session, buffer, Menu button state machine§7, §8

"It compiles on tvOS" means nothing. These five areas compile fine and fail at runtime.

When to Use This Skill

  • Building a new tvOS app or adding tvOS target
  • Porting an iOS app to tvOS
  • Debugging focus, remote input, or storage issues on tvOS
  • Working with AVPlayer, TVUIKit, or text input on tvOS

Example Prompts

These are real questions developers ask that this skill answers:

1. "I'm porting my iOS app to tvOS and focus navigation doesn't work"

-> The skill explains the dual focus system (UIKit Focus Engine vs @FocusState) and common traps

2. "My tvOS app loses all data between launches"

-> The skill explains there is no persistent local storage and shows the iCloud-first pattern

3. "How do I handle Siri Remote input in SwiftUI on tvOS?"

-> The skill covers both generations of remote and the three input layers (SwiftUI, UIKit gestures, GameController)

4. "WebView doesn't work on tvOS, how do I display web content?"

-> The skill shows JavaScriptCore for parsing and native rendering alternatives

Red Flags

If ANY of these appear, STOP:

  • "I'll just use the same storage code as iOS" — tvOS has no Document directory
  • "WebView will work for this" — No WebView on tvOS at all (Apple HIG: "Not supported in tvOS")
  • "@FocusState handles focus" — tvOS has a dual focus system; @FocusState alone is incomplete
  • "I'll save to Application Support" — It's Cache-only; the system deletes files when app is not running
  • "Standard UITextField will work" — tvOS text input triggers a fullscreen keyboard; consider the shadow input pattern
  • "I'll just use the same AVPlayer code" — tvOS needs.ambient audio session on launch, custom Menu button handling, and buffer tuning. Default iOS AVPlayer setup causes audio session conflicts and broken back navigation.

1. Focus Engine vs @FocusState

tvOS has two focus systems that must coexist. This is the #1 source of confusion for iOS developers.

The Dual System

SystemControlsAPI
UIKit Focus EngineHardware remote navigation, directional scanningUIFocusEnvironment, UIFocusSystem, UIFocusGuide
SwiftUI FocusProgrammatic focus binding, focus sections@FocusState,.focused(),.focusable(),.focusSection()

When Each Applies

User swipes on remote → UIKit Focus Engine handles it (always)
Code sets @FocusState → SwiftUI handles it (sometimes overridden by Focus Engine)

The trap: @FocusState can set focus programmatically, but the UIKit Focus Engine is the ultimate authority. If the Focus Engine considers a view unfocusable, @FocusState assignments are silently ignored.

UIKit Focus Engine API

The UIFocusEnvironment protocol (implemented by UIView, UIViewController, UIWindow) provides:

class MyViewController: UIViewController {
    // Priority-ordered list of where focus should go
    override var preferredFocusEnvironments: [UIFocusEnvironment] {
        [preferredButton, fallbackButton]
    }

    // Validate proposed focus changes
    override func shouldUpdateFocus(
        in context: UIFocusUpdateContext
    ) -> Bool {
        // Return false to block focus movement
        return context.nextFocusedView != disabledButton
    }

    // Respond to completed focus changes
    override func didUpdateFocus(
        in context: UIFocusUpdateContext,
        with coordinator: UIFocusAnimationCoordinator
    ) {
        coordinator.addCoordinatedAnimations {
            context.nextFocusedView?.transform = CGAffineTransform(scaleX: 1.1, y: 1.1)
            context.previouslyFocusedView?.transform = .identity
        }
    }

    // Request focus update (async)
    func moveFocusToPreferred() {
        setNeedsFocusUpdate()      // Schedule update
        updateFocusIfNeeded()       // Execute immediately
    }
}

UIFocusGuide — Bridging Navigation Gaps

When focusable views aren't in a direct grid layout, the Focus Engine can't find them by scanning directionally. UIFocusGuide creates invisible focusable regions that redirect to real views:

let focusGuide = UIFocusGuide()
view.addLayoutGuide(focusGuide)

// Position the guide between two non-adjacent views
NSLayoutConstraint.activate([
    focusGuide.leadingAnchor.constraint(equalTo: leftButton.trailingAnchor),
    focusGuide.trailingAnchor.constraint(equalTo: rightButton.leadingAnchor),
    focusGuide.topAnchor.constraint(equalTo: leftButton.topAnchor),
    focusGuide.heightAnchor.constraint(equalTo: leftButton.heightAnchor)
])

// When focus enters the guide, redirect to the target view
focusGuide.preferredFocusEnvironments = [rightButton]

SwiftUI Focus API

struct ContentView: View {
    @FocusState private var focusedItem: MenuItem?

    var body: some View {
        VStack {
            ForEach(MenuItem.allCases) { item in
                Button(item.title) { select(item) }
                    .focused($focusedItem, equals: item)
            }
        }
        .focusSection()       // Group focusable items for navigation
        .defaultFocus($focusedItem, .home)  // Set initial focus
    }
}

Key SwiftUI focus modifiers for tvOS:

  • .focused(_:equals:) — Bind focus to a value
  • .focusable() — Make custom views focusable
  • .focusSection() — Group related items for directional navigation
  • .defaultFocus(_:_:) — Set where focus starts in a scope

Default Focusable Elements

UIButton, UITextField, UITableViewCell, and UICollectionViewCell are focusable by default. Custom views need canBecomeFocused (UIKit) or .focusable() (SwiftUI). The top-left item receives initial focus at launch.

Common Focus Gotchas

GotchaSymptomFix
Non-focusable containerSwipe skips your viewAdd .focusable() or override canBecomeFocused
Focus guide missingCan't navigate to isolated viewAdd UIFocusGuide to bridge the gap
@FocusState ignoredProgrammatic focus doesn't workCheck preferredFocusEnvironments chain
Focus update not requestedFocus stays stale after layout changeCall setNeedsFocusUpdate() + updateFocusIfNeeded()
Items not in grid layoutFocus jumps unpredictablyArrange focusable items in a grid or use focus guides
UIHostingConfiguration focusFocus corruption in mixed UIKit/SwiftUIKnown issue — test UIHostingConfiguration cells carefully

2. Siri Remote Input

Two generations with different hardware — your code must handle both.

Generation Differences

FeatureGen 1 (2015-2021)Gen 2 (2021+)
Top surfaceTouchpad (full swipe)Clickpad + outer touch ring
Swipe gesturesFull areaRing edge only
Click navigationCenter pressD-pad style
AccelerometerYesYes

Standard SwiftUI Modifiers (Preferred)

For most UI, SwiftUI handles remote input automatically through the focus system:

Button("Play") { startPlayback() }
    .focused($isFocused)  // Automatically responds to remote navigation

List(items) { item in
    Text(item.title)
}
// List navigation works automatically with remote
// Note: First item receives focus by default on tvOS — use .defaultFocus() to override

Gesture Recognizers (UIKit)

Detect specific button presses and gestures via UIKit recognizers:

// Detect Play/Pause button
let playPause = UITapGestureRecognizer(target: self, action: #selector(handlePlayPause))
playPause.allowedPressTypes = [NSNumber(value: UIPress.PressType.playPause.rawValue)]
view.addGestureRecognizer(playPause)

// Detect swipe on touchpad
let swipe = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe))
swipe.direction = .right
view.addGestureRecognizer(swipe)

Available UIPress.PressType values: .menu, .playPause, .select, .upArrow, .downArrow, .leftArrow, .rightArrow, .pageUp, .pageDown

Low-Level Press Handling

For fine-grained control, override UIResponder press methods:

override func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
    for press in presses {
        if press.type == .select {
            handleSelectDown()
        }
    }
}

override func pressesEnded(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
    for press in presses {
        if press.type == .select {
            handleSelectUp()
        }
    }
}

// Always implement all four: pressesBegan, pressesEnded, pressesChanged, pressesCancelled

Game Controller Framework (Raw Input)

For custom interactions (scrubbing, games), access the Siri Remote as a GCMicroGamepad:

import GameController

NotificationCenter.default.addObserver(
    forName: .GCControllerDidConnect, object: nil, queue: .main
) { notification in
    guard let controller = notification.object as? GCController,
          let micro = controller.microGamepad else { return }

    // Touchpad as analog D-pad (-1.0 to 1.0)
    micro.dpad.valueChangedHandler = { _, xValue, yValue in
        handleRemoteInput(x: xValue, y: yValue)
    }

    // reportsAbsoluteDpadValues: true = absolute position, false = relative movement
    micro.reportsAbsoluteDpadValues = false

    // allowsRotation: true = values adjust when remote is rotated
    micro.allowsRotation = false

    // Face buttons
    micro.buttonA.pressedChangedHandler = { _, _, pressed in }
    micro.buttonX.pressedChangedHandler = { _, _, pressed in }
    micro.buttonMenu.pressedChangedHandler = { _, _, pressed in }
}

Progress Bar Scrubbing

UIPanGestureRecognizer with virtual damping for smooth seeking:

let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan))

@objc func handlePan(_ gesture: UIPanGestureRecognizer) {
    let velocity = gesture.velocity(in: view)
    let dampingFactor: CGFloat = 0.002  // Tune for feel

    switch gesture.state {
    case .changed:
        let seekDelta = velocity.x * dampingFactor
        player.seek(to: currentTime + seekDelta)
    default:
        break
    }
}

3. Storage Constraints

This is the most dangerous iOS assumption on tvOS. tvOS has no Document directory. All local storage is Cache that the system can delete at any time. Skipping iCloud integration means 2-3 weeks debugging intermittent "data disappears" bugs that only happen on real devices between app launches.

From Apple's App Programming Guide for tvOS: "Every app developed for the new Apple TV must be able to store data in iCloud and retrieve it in a way that provides a great customer experience."

What tvOS Has

DirectoryExists?Persistent?
DocumentsNoN/A
Application SupportYesNo — system can delete when app is not running
CachesYesNo — system deletes under storage pressure
tmpYesNo

Size Limits

  • App bundle: 4 GB maximum
  • NSUserDefaults / UserDefaults: 500 KB maximum (Apple docs; not the 4 MB iOS gets). Available but subject to system purge — not guaranteed persistent between sessions
  • On-demand resources: Available for read-only assets the OS manages
  • Local cache: No guaranteed size; system can purge while app is not running

What This Means

  • Every local file can vanish between app launches
  • SQLite databases stored locally will be deleted
  • Your app must survive with zero local data
  • Downloaded data is NOT deleted while the app is running — only between sessions

Recommended Pattern

// ✅ CORRECT: iCloud as primary, local as cache only
func loadData() async throws -> [Item] {
    // 1. Try iCloud first (persistent)
    if let cloudData = try? await fetchFromICloud() {
        // Cache locally for offline use
        try? cacheLocally(cloudData)
        return cloudData
    }

    // 2. Fall back to local cache (may not exist)
    if let cached = try? loadFromLocalCache() {
        return cached
    }

    // 3. Start fresh — this is normal on tvOS
    return []
}

Database Recommendations

SolutiontvOS ViabilityNotes
SQLiteData + CloudKit SyncEngineRecommendediCloud is persistent; local is just cache
SwiftData + CloudKitWorks, but fragileNo persistent local-only storage; ModelContainer must be configured for CloudKit from day one — adding sync later requires migration; system database deletion triggers full re-sync on next launch
CoreData + CloudKitDangerousSpace inflation from CloudKit metadata
Local-only GRDB/SQLiteUnreliableSystem deletes the database file
NSUbiquitousKeyValueStoreGood for small data1 MB limit, key-value only
On-demand resourcesGood for read-only assetsOS manages download/purge lifecycle

See axiom-sqlitedata for CloudKit SyncEngine patterns, axiom-storage for full storage decision tree.


4. No WebView

tvOS has no WKWebView, no SFSafariViewController, no WebView. Apple HIG explicitly states: web views are "Not supported in tvOS."

What You Can Do

NeedSolution
Parse HTML/JSONUse JavaScriptCore (JSContext, JSValue — no DOM)
Display web contentRender natively from parsed data
HLS streaming from m3u8Local HTTP server pattern (see below)
OAuth loginDevice code flow (RFC 8628) or companion device

JavaScriptCore for Parsing

JavaScriptCore provides a JavaScript execution engine without DOM or web rendering. Available on tvOS.

import JavaScriptCore

let context = JSContext()!

// Evaluate scripts
context.evaluateScript("""
    function parsePlaylist(m3u8Text) {
        return m3u8Text.split('\\n')
            .filter(line => !line.startsWith('#'))
            .filter(line => line.trim().length > 0);
    }
""")

// Pass data safely via setObject (avoids injection)
context.setObject(m3u8Content, forKeyedSubscript: "rawContent" as NSString)
let result = context.evaluateScript("parsePlaylist(rawContent)")

// Convert back to Swift types
let segments = result?.toArray() as? [String] ?? []

Key classes: JSVirtualMachine (execution environment), JSContext (script evaluation), JSValue (type bridging)

Limitation: No DOM, no web rendering, no fetch/XMLHttpRequest. Pure JavaScript execution only.

Local HTTP Server for HLS

When you need to serve modified m3u8 playlists to AVPlayer:

// Use Swifter (httpswift/swifter) or GCDWebServer
// Serve rewritten m3u8 on localhost, point AVPlayer to it
let localURL = URL(string: "http://localhost:8080/playlist.m3u8")!
let playerItem = AVPlayerItem(url: localURL)

5. TVUIKit Components

tvOS-exclusive UIKit components. Bridge to SwiftUI via UIViewRepresentable.

TVPosterView

Media content display with built-in focus expansion and parallax:

import TVUIKit

let poster = TVPosterView(image: UIImage(named: "moviePoster"))
poster.title = "Movie Title"
poster.subtitle = "2024"

// Focus expansion and parallax happen automatically
// Access the underlying image view:
poster.imageView.adjustsImageWhenAncestorFocused = true

TVLockupView

Base class for TVPosterView — a flexible container managing content with focus behavior:

let lockup = TVLockupView()
lockup.contentView.addSubview(customView)
lockup.headerView = headerFooter   // TVLockupHeaderFooterView
lockup.footerView = footerFooter
// showsOnlyWhenAncestorFocused: header/footer visibility on focus

Other TVUIKit Components

ComponentPurpose
TVCardViewSimple container with customizable background
TVCaptionButtonViewButton with image + text + directional parallax
TVMonogramViewUser initials/image with PersonNameComponents
TVCollectionViewFullScreenLayoutImmersive full-screen collection with parallax + masking
TVMediaItemContentViewContent configuration with badges, playback progress

TVDigitEntryViewController

System-provided passcode/PIN entry (tvOS 12+):

let digitEntry = TVDigitEntryViewController()
digitEntry.numberOfDigits = 4
digitEntry.titleText = "Enter PIN"
digitEntry.promptText = "Enter your parental control code"
digitEntry.isSecureDigitEntry = true

present(digitEntry, animated: true)

digitEntry.entryCompletionHandler = { pin in
    guard let pin else { return }  // User cancelled
    authenticate(with: pin)
}

// Reset entry
digitEntry.clearEntry(animated: true)

6. Text Input on tvOS

tvOS text input is fundamentally different from iOS. Apple recommends minimizing text input in your UI.

Three Approaches

ApproachBest ForKeyboard Style
UIAlertControllerQuick, simple inputModal with text field
UITextFieldMulti-field formsFullscreen keyboard with Next/Previous
UISearchControllerSearchInline single-line keyboard

UITextField (Fullscreen Keyboard)

The primary text input method. Calling becomeFirstResponder() presents a fullscreen keyboard:

let textField = UITextField()
textField.placeholder = "Enter name"
textField.becomeFirstResponder()  // Presents keyboard immediately
// Done button returns user to previous page
// Built-in Next/Previous buttons navigate between text fields

Shadow Input Pattern (SwiftUI)

When you want a custom-styled input trigger in SwiftUI:

struct TVTextInput: View {
    @State private var text = ""
    @State private var isEditing = false

    var body: some View {
        Button {
            isEditing = true
        } label: {
            HStack {
                Text(text.isEmpty ? "Search..." : text)
                    .foregroundStyle(text.isEmpty ? .secondary : .primary)
                Spacer()
                Image(systemName: "keyboard")
            }
            .padding()
            .background(.quaternary)
            .clipShape(RoundedRectangle(cornerRadius: 10))
        }
        .sheet(isPresented: $isEditing) {
            TVKeyboardSheet(text: $text)
        }
    }
}

UISearchController (Inline Keyboard)

For search interfaces — all input on a single line, but very limited customization:

let searchController = UISearchController(searchResultsController: resultsVC)
searchController.searchResultsUpdater = self
// Cannot customize text traits or add input accessories

SwiftUI .searchable()

SwiftUI's .searchable() modifier works on tvOS and presents the system search keyboard. Use it for standard search patterns:

NavigationStack {
    List(filteredItems) { item in
        Text(item.title)
    }
    .searchable(text: $searchText, prompt: "Search movies")
}

For custom search UI beyond what .searchable() offers, fall back to the shadow input pattern above.


7. AVPlayer Tuning

tvOS media apps need specific AVPlayer configuration for good UX.

Essential Settings

let player = AVPlayer(url: streamURL)

// automaticallyWaitsToMinimizeStalling defaults to true (iOS 10+/tvOS 10+)
// Set false for immediate playback when synchronizing players
// or when you want playback to start ASAP from a non-empty buffer
player.automaticallyWaitsToMinimizeStalling = false

// Buffer hint — 0 means system chooses automatically
// Higher values reduce stalling risk but consume more memory
player.currentItem?.preferredForwardBufferDuration = 30

// Audio session — don't interrupt other apps' audio on launch
try AVAudioSession.sharedInstance().setCategory(.ambient)
// Switch to .playback when user presses play

Custom Dismiss Logic

The default swipe-down gesture dismisses the player. Override for media apps:

class PlayerViewController: AVPlayerViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        // Handle Menu button for custom back navigation
        let menuPress = UITapGestureRecognizer(
            target: self, action: #selector(handleMenu)
        )
        menuPress.allowedPressTypes = [
            NSNumber(value: UIPress.PressType.menu.rawValue)
        ]
        view.addGestureRecognizer(menuPress)
    }

    @objc func handleMenu() {
        if isShowingControls {
            hideControls()
        } else {
            dismiss(animated: true)
        }
    }
}

8. Menu Button State Machine

The Siri Remote Menu button doubles as "back" and "dismiss." Media apps need a state machine to handle it correctly.

The Problem

State: Playing with controls visible
  Menu press → Hide controls (not dismiss)

State: Playing with controls hidden
  Menu press → Show "are you sure?" or dismiss

State: In submenu/settings overlay
  Menu press → Close overlay (not dismiss player)

Pattern

enum PlayerState {
    case loading        // Buffering / loading content
    case playing        // Controls hidden
    case controlsShown  // Controls visible
    case submenu        // Settings/subtitles overlay
}

func handleMenuPress(in state: PlayerState) -> PlayerState {
    switch state {
    case .submenu:
        dismissSubmenu()
        return .controlsShown
    case .controlsShown:
        hideControls()
        return .playing
    case .playing:
        dismiss(animated: true)
        return .playing
    case .loading:
        cancelLoading()
        dismiss(animated: true)
        return .loading
    }
}

9. Network Differences

IPv6 Priority

Apple TV strongly prefers IPv6. All App Store apps must support IPv6-only networks (DNS64/NAT64). If your backend is IPv4-only, connections may be slower or fail on some networks.

Device Performance Variance

DeviceChipRAMNotes
Apple TV HD (4th gen)A82 GBStill supported; much slower
Apple TV 4K (1st gen)A10X3 GBCapable
Apple TV 4K (2nd gen)A124 GBGood
Apple TV 4K (3rd gen)A154 GBExcellent

Test on older hardware. The Apple TV HD is still in use and dramatically slower than 4K models.


10. Developer Experience

Debug-Only Input Macros

Test without Siri Remote in Simulator using keyboard shortcuts:

#if DEBUG
extension View {
    func debugOnlyModifier() -> some View {
        self.onKeyPress(.space) {
            print("Space pressed — simulating select")
            return .handled
        }
    }
}
#endif

View Inspection Helper

#if DEBUG
extension View {
    func debugBorder() -> some View {
        border(.red, width: 1)
    }
}
#endif

Simulator Limitations

  • Simulator does not accurately simulate Focus Engine behavior
  • Always test focus navigation on a real Apple TV device
  • Simulator keyboard input!= Siri Remote input
  • Performance profiling must happen on device (especially Apple TV HD)

Anti-Rationalization

ThoughtReality
"I'll just use the same code as iOS"tvOS diverges in storage, focus, input, and web views. You will hit walls.
"Focus works like iOS"tvOS has a dual focus system (UIKit Focus Engine + SwiftUI @FocusState). @FocusState alone is insufficient.
"Local storage is fine for now"There is no persistent local storage on tvOS. Apple requires iCloud capability.
"WebView will work"Apple HIG: web views are "Not supported in tvOS." JavaScriptCore only (no DOM).
"I'll handle text input with TextField"UITextField triggers a fullscreen keyboard. Consider shadow input pattern or UISearchController for better UX.
"I only need to test on Simulator"Focus Engine and performance require real device testing.

Resources

Source: "Surviving tvOS" (Ronnie Wong, 2026) — tvOS engineering log for Syncnext media player

Apple Docs: /tvuikit, /uikit/uifocusenvironment, /uikit/uifocusguide, /swiftui/focus, /gamecontroller/gcmicrogamepad, /avfoundation/avplayer, /javascriptcore

Apple Guides: App Programming Guide for tvOS (storage, input, gestures), HIG Web Views (tvOS exclusion)

WWDC: 2016-215, 2017-224, 2021-10023, 2021-10081, 2021-10191, 2023-10162, 2025-219

Skills: axiom-storage, axiom-sqlitedata, axiom-avfoundation-ref, axiom-hig-ref, axiom-liquid-glass

平台分布

Codex

34.26%
按下载量换算255

Claude

32.25%
按下载量换算240

Cursor

16.68%
按下载量换算124

Gemini CLI

8.26%
按下载量换算62

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills