惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The Cloudflare Blog
Microsoft Security Blog
Microsoft Security Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
L
LangChain Blog
W
WeLiveSecurity
P
Proofpoint News Feed
月光博客
月光博客
NISL@THU
NISL@THU
L
LINUX DO - 最新话题
Webroot Blog
Webroot Blog
T
Threatpost
Y
Y Combinator Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
T
Threat Research - Cisco Blogs
Vercel News
Vercel News
Jina AI
Jina AI
阮一峰的网络日志
阮一峰的网络日志
S
Schneier on Security
J
Java Code Geeks
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
小众软件
小众软件
MyScale Blog
MyScale Blog
N
News and Events Feed by Topic
Stack Overflow Blog
Stack Overflow Blog
有赞技术团队
有赞技术团队
The Hacker News
The Hacker News
Schneier on Security
Schneier on Security
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Help Net Security
Help Net Security
Recent Announcements
Recent Announcements
S
Security @ Cisco Blogs
C
CXSECURITY Database RSS Feed - CXSecurity.com
S
Securelist
T
The Exploit Database - CXSecurity.com
云风的 BLOG
云风的 BLOG
C
Cisco Blogs
雷峰网
雷峰网
量子位
Google DeepMind News
Google DeepMind News
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Spread Privacy
Spread Privacy
L
Lohrmann on Cybersecurity
I
Intezer
T
The Blog of Author Tim Ferriss
G
GRAHAM CLULEY
D
DataBreaches.Net
V
Vulnerabilities – Threatpost
P
Privacy & Cybersecurity Law Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
罗磊的独立博客

肘子的 Swift 记事本 | Fatbobman's Blog

当每一次写入都有了新价格 -- 肘子的 Swift 周报 #144 当 Linux 成为“空气”:容器、Agent 与不再重要的“桌面之争” -- 肘子的 Swift 周报 #143 两个 SwiftUI 动画 Bug 排查小记 SPI 加入 Apple,Swift 迈向自举 -- 肘子的 Swift 周报 #142 Swift 还让你 Excited 吗? -- 肘子的 Swift 周报 #141 从 Size Class 到可用空间,horizontalSizeClass 还可靠吗? WWDC 26:AI 帮你看完了,然后呢? -- 肘子的 Swift 周报 #140 WWDC 2026 初印象:符合预期,但更务实 -- 肘子的 Swift 周报 #139 Core Data + Observation:从属性级响应到心智解放 稳定 > 新功能 -- 肘子的 Swift 周报 #138 用自定义 Layout 化解 SwiftUI List 的行高与间距跳变 从社区路标到生态基石:Dave Verwer 的新篇章 -- 肘子的 Swift 周报 #137 消失的 WWDC 愿望单 -- 肘子的 Swift 周报 #136 CocoaPods 正在退场,SwiftPM 才刚到第二章 -- 肘子的 Swift 周报 #135 让 AI 从称手到称心 -- 肘子的 Swift 周报 #134 Swift 并发正被更广泛地接纳 -- 肘子的 Swift 周报 #133 从 OpenSwiftUI 到 DanceUI:换个方式 Dive SwiftUI -- 肘子的 Swift 周报 #132 被 Vibe 摧毁的版权壁垒,与开发者的新护城河 -- 肘子的 Swift 周报 #131 苹果的罕见妥协:当高危漏洞遇上“拒升”潮 -- 肘子的 Swift 周报 #130 一墙之隔,不同的时空 -- 肘子的 Swift 周报 #129 我的 App 审核被卡了? -- 肘子的 Swift 周报 #128 CDE:一次让 Core Data 更像现代 Swift 的尝试 50 岁的苹果和 51 岁的我 -- 肘子的 Swift 周报 #127 2026 年,为什么我仍在思考 Core Data Macbook Neo:苹果重回校园的起点 -- 肘子的 Swift 周报 #126 跨域传递 NSManagedObjectContext 为什么在 Swift 6.2 中不再报错?真正的变化不在编译器 Xcode 26.3 + Claude Agent:模型替换、MCP、Skill 与自适应配置 macOS 录屏软件开发实录:从像素抓取到元数据重现 isolated(any) 与 #isolation:让 Swift 闭包自动继承隔离域 告别“可移植汇编”:我已让 Swift 在 MCU 上运行七年 独立开发者的试炼:Zipic 从 0 到 1 的产品化之路 逃离 Mac App Store:如何从零构建独立应用的分发与售卖体系 为什么父视图无法修改子视图的 @State Partytown 实战:修复 GTM 自定义事件与 dataLayer 失效问题 Swift 6 编译加速:启用 Swift-Syntax 预构建 (Prebuilts) Swift 模块化:使用 package 关键字实现 Target 间安全共享 Monorepo 实战:解决 VSCode/Cursor 中 SwiftLint 配置失效问题 VSCode/Cursor 测试 Core Data:手动编译 xcdatamodeld 指南
在 tvOS 上活下來:一個非典型播放器的工程實錄
Ronnie Wong · 2026-01-14 · via 肘子的 Swift 记事本 | Fatbobman's Blog

在苹果庞大的硬件生态中,除了 iPhone 和 MacBook 这些自带光环的明星产品外,还有一些设备在其特定领域占据着统治地位,却往往被开发者的视野所忽略。Apple TV 显然就是其中最典型的代表——它存在感不强,让很多开发者对其“投入产出比”心存疑虑。

由于 SwiftUI 的跨平台特性,我曾天真地以为开发 tvOS 应用只是 iOS 开发的简单延伸。但在过去两年与 Ronnie Wong 的交流中,我才意识到 tvOS 对于大多数开发者来说,无论从设计理念、硬件限制,还是开发调试细节,都是一个充满挑战的全新领域。

因此,我特邀 Ronnie Wong 分享她在开发 Syncnext 过程中的工程实录。这篇文章不仅展现了苹果生态中这个“小众”平台的独特开发场景,更是她在这个领域“活下来”的经验总结。希望能借此通过真实的视角,让更多开发者了解 Apple TV 开发的苦与乐。

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

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

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

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

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

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 鍵 -> 退出播放。

3. AVPlayer 調校

AVPlayer 默認是一個「暴力」的網絡播放器,需要針對性調優:

  • 降低延遲
player?.automaticallyWaitsToMinimizeStalling = true
player?.currentItem?.preferredForwardBufferDuration = 0 // 直播/串流建議
  • 本地文件特化
// 若是本地文件
automaticallyWaitsToMinimizeStalling = false
preferredForwardBufferDuration = 1
  • HLS 支持:AVPlayer 強制要求 HTTP(s) 協議。如果你需要動態重建 m3u8,必須使用 Local Server (推薦 Swifter 框架)。

  • 動態背景:若將 AVPlayer 用於動態背景,務必設置 AVAudioSession.ambient 以免打斷系統音頻。

4. 自定義 Dismiss 行為

在 tvOS 新版本中,AVPlayer 對 dismiss 的響應發生了變化。建議重寫 dismiss 邏輯,依序檢查: presentingViewController (dismiss) -> navigationController (pop) -> removeFromParent

五、數據同步策略

不推薦: CoreData with CloudKit

在 2026 年的視角下,我不建議使用 CoreData with CloudKit 方案。它始終無法解決空間膨脹的問題,且在無持久存儲的 tvOS 上風險極高。

肘子注

这里所说的空间膨胀,指的是数据经由 NSPersistentCloudKitContainer 同步后,云端占用往往会远超本地 SQLite 数据库的大小。这是由类型转换、元数据开销等原因造成的。一旦你的应用需要保存大量二进制数据,这个问题就会被成倍放大,甚至可能导致用户的 iCloud 空间爆满。

当时我们在 Discord 上对此有过专门的讨论

推薦:sqlite-data

我推薦使用 sqlite-data 作為 iCloud 支持方案。它在 TracklyReborn 項目中經過測試,表現穩定且可控。

六、後記:關於 eisonAI

在結束這篇硬核的 tvOS 工程實錄前,想介紹一下我的最新作品 —— eisonAI

iPhone-Medata-Preview.jpg

這是一個關於「記憶」與「心流」的 App。我們常遇到看過東西卻找不到、想法一寫就亂的情況。eisonAI 引入了 Cognitive Index™(認知索引) 的概念:不只記住「內容」,更記住「用途」。它就像你的腦內圖書館管理員,幫你把靈感、背景資料、引用文獻自動歸位,保護你的思考心流不被打斷。

订阅 Fatbobman 周报

每周精选 Swift 与 SwiftUI 开发技巧,加入众多开发者的行列。

立即订阅