Writing

在 tvOS 上活下來:一個非典型播放器的工程實錄

tvOS,Player,SwiftUI · 2026-01-08 · Updated 2026-04-26

序言

如果你只是寫一個 Demo,tvOS 看起來其實不難;但只要嘗試做一個需要長期維護、面對真實用戶環境的 App,很快就會發現:tvOS 從來就不是 iPad 的放大版

它幾乎沒有可靠的本地持久儲存,隨時可能被系統清空資料或殺掉行程;整個交互模型以 Focus 為中心,沒有瀏覽器、沒有 WebView,卻又在播放器層面深度依賴整個 Web 世界。從設計上來看,tvOS 本質上是一個展示終端,而不是工作平台。

Syncnext 正好是一個非典型的例子:它不是服務單一後端的定制播放器,而是在 tvOS 上嘗試面對真實而混亂的網路環境。這篇文章不是教學,而是一次工程實錄,整理那些在這個平台上「活下來」所付出的設計取捨與現實妥協。

如果你正在考慮做一個不只是 Demo 的 tvOS App,這些經驗或許能幫你少走一些彎路。


目錄

  • 序言:為什麼是「活下來」
  • 一、核心交互哲學:非接觸式操作
    • Focus Engine vs. SwiftUI @FocusState
    • 深入支持遙控器:從 SwiftUI 到 Game Controller Framework
  • 二、系統限制與生存指南
    • 嚴苛的存儲環境:沒有持久儲存的現實
    • 缺失的 Web 能力:沒有 WebView 的 tvOS
    • 網絡與設備差異:IPv6 與性能斷層
    • 本地化與故障排除:語言、黑屏與重啟玄學
  • 三、SwiftUI 在 tvOS 上的實戰填坑
    • 輸入框的「影子戰術」
    • 導航與狀態管理的現實
    • 為什麼你最終還是需要 TVUIKit
    • 焦點系統混用的災難
    • 開發者體驗優化(Debug / 可視化)
  • 四、播放器核心技術細節
    • 絲滑的進度條交互設計
    • 雙重 UI 疊加與 Back 鍵狀態機
    • AVPlayer 調校策略
    • HLS、Local Server 與動態背景
    • 自定義 Dismiss 行為
  • 五、數據同步策略
    • 為什麼應該放棄 CoreData with CloudKit
    • 推薦方案:sqlite-data
  • 六、後記:關於 eisonAI

一、核心交互哲學:非接觸式操作

tvOS 的 UI/UX 概念,與「觸摸式」系統或「鼠標模式」系統截然不同。你需要使用 Game UX 的概念來思考。在主機平台上,手柄的交互模式才是開發 tvOS App 的首要參考對象。

1. Focus Engine vs. SwiftUI @FocusState

在 tvOS 開發中,你會遇到兩套焦點系統:

  • Focus Engine: 以 UIKit 為中心,專為 tvOS 設計的原生方案。
  • SwiftUI @FocusState: 以 Apple 全平台為中心,跨平台的狀態管理系統。

請注意,它們是完全不同概念的產品。 雖然交互形式相似,但邏輯不同。

  • 常見陷阱:你可以在 UI 上同時使用 @FocusState 和 Focus Engine 將兩個焦點高亮,但系統認可的「真實焦點」只是你最後操作的那個指令。

2. 深入支持遙控器

tvOS 的遙控器主要分為兩代:

  1. Siri Remote (1st Gen): 觸控板為主。
  2. Siri Remote (2nd Gen): 實體按鍵 + 觸控環。

如果你不在意那些喜歡舊款遙控器的老用戶,完全可以使用 SwiftUI 的標準修飾符。但如果你追求極致體驗,你需要使用 Game Controller Framework 深入到 RAW 層去處理輸入訊號。


二、系統限制與生存指南

1. 嚴苛的存儲環境

  • 沒有持久儲存:你沒有寫入 Document 目錄的權限,只能寫入 Cache 文件夾。
  • 數據隨時遺失:當 tvOS 監測到存儲空間緊張時,系統會隨機刪除 App 的 Cache 數據。如果你的 App 以本地 SQLite 數據庫為核心,這將是災難性的。
    • 常見現象:「因為你的 Apple TV 空間不足,所以刪除了這個 App 的數據庫。」
    • 建議檢查:鳥瞰壁紙/iCloud 圖片是否開啟?Infuse/SenPlayer 的緩存是否設置為內存模式?

2. 缺失的 Web 能力

  • 沒有瀏覽器:tvOS 沒有 WebView,只有一個 JavaScriptCore 框架。依賴 DOM 運作的概念在這裡無法實現。

3. 網絡與設備差異

  • IPv6 優先:在同一局域網下,可能出現手機可訪問 API,但 tvOS 因 IPv6 策略而無法訪問的情況。
  • 算力斷層:Apple TV 4K (2017, 2021, 2022) 之間的性能差異巨大。如果你做了酷炫的視覺效果,請務必為老設備提供「關閉特效」的選項。

4. 本地化與故障排除

  • 無簡體中文 Siri:使用 Siri 語音輸入時,系統只會識別並輸出「繁體中文」。如果你的 API 需要簡體中文,必須在 App 內置「繁簡轉換」系統。
  • 終極大法:如果 App 在某些用戶設備上無法啟動(黑屏或閃退),這通常不是你的代碼問題。請建議用戶 「拔電重啟」 Apple TV。

三、SwiftUI 在 tvOS 上的實戰填坑

1. 輸入框的「影子戰術」

SwiftUI 的 TextField 在 tvOS 上既醜又難用。解決方案是**「影子輸入法」**:

  • UI 層:用漂亮的 Button 製作 UI。
  • 功能層:在視圖背景放置一個透明的 CocoaTextField (UIKit)。
  • 交互:點擊按鈕時,調用 textField.becomeFirstResponder() 喚起鍵盤。

2. 導航與狀態管理

  • Searchable 節流:tvOS 會根據輸入頻率觸發搜索,務必實施節流 (Throttle) 並緩存結果。
  • 自定義 NavigationLink:SwiftUI 原生導航缺乏靈活性,建議封裝 .navigate(using:destination:) 修飾符,實現「狀態驅動」的導航跳轉。

3. TVUIKit 的必要性

SwiftUI 的 .card 樣式往往給人一種「山寨」感。要開發具有官方質感的 App,你需要封裝 TVUIKit 組件:

  • 排版:用 UILabel 實現精準行高控制;用 UITextView 實現遙控器滾動閱讀。
  • 視覺:用 TVPosterView 獲取原生的視差與聚焦效果。
  • 輸入:用 TVDigitEntryViewController 實現原生的數字密碼輸入。

4. 焦點系統的混用災難

在 tvOS 上混用 SwiftUI 與 UITableView 時,SwiftUI 的焦點預測機制常與列表實際高度衝突,導致焦點錯位。解決方案通常是直接接管 UITableView 的 Focus 系統。

5. 開發者體驗優化


四、播放器核心技術細節

1. 絲滑的進度條交互

基於 UIPanGestureRecognizer 實現。電視遙控器的觸控板不是鼠標,不能直接映射位移。

  • 虛擬阻尼:設置 location / 5 的比例係數。這個「手感值」將位移進行非線性縮放,確保在大長度視頻中也能精確定位到秒。
  • 實時反饋:滑動時狀態需直接驅動 UI Overlay,實現「所見即所得」。

2. 雙重 UI 疊加與 Back 鍵邏輯

Menu (Back) 鍵的邏輯是多維的,需要一個狀態機:

  1. 狀態 A (UI 顯示中):如果焦點在按鈕上,按 Menu 鍵 -> 焦點回到進度條。
  2. 狀態 B (焦點在進度條):按 Menu 鍵 -> 隱藏 UI。
  3. 狀態 C (UI 已隱藏):按 Menu 鍵 -> 退出播放。