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 的遙控器主要分為兩代:
- Siri Remote (1st Gen): 觸控板為主。
- 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. 開發者體驗優化
- Debug 輸入:開發過程中無法高效輸入文字,建議用
#if DEBUG宏包裹專用按鈕,一鍵填充測試關鍵字。 - 可視化調試:使用自定義的
debugOnlyModifier高亮 View 的尺寸和位置。 - 延伸閱讀:我的 Fuck SwiftUI 筆記
四、播放器核心技術細節
1. 絲滑的進度條交互
基於 UIPanGestureRecognizer 實現。電視遙控器的觸控板不是鼠標,不能直接映射位移。
- 虛擬阻尼:設置
location / 5的比例係數。這個「手感值」將位移進行非線性縮放,確保在大長度視頻中也能精確定位到秒。 - 實時反饋:滑動時狀態需直接驅動 UI Overlay,實現「所見即所得」。
2. 雙重 UI 疊加與 Back 鍵邏輯
Menu (Back) 鍵的邏輯是多維的,需要一個狀態機:
- 狀態 A (UI 顯示中):如果焦點在按鈕上,按 Menu 鍵 -> 焦點回到進度條。
- 狀態 B (焦點在進度條):按 Menu 鍵 -> 隱藏 UI。
- 狀態 C (UI 已隱藏):按 Menu 鍵 -> 退出播放。