Files
yattee/YatteeTopShelf/TopShelfSection.swift
Arkadiusz Fal 9f86ff0667 Add tvOS Top Shelf extension
Surfaces Continue Watching, Recent Feed, and Recent Bookmarks in the
Apple TV Home top shelf when Yattee is focused. Tapping a tile opens
the video via the existing yattee://video/{id} deep link.

- New YatteeTopShelf app extension target (tvOS only). LD_ENTRY_POINT is
  overridden to _NSExtensionMain; the tv-app-extension product type
  defaults to _TVExtensionMain which is for the pre-tvOS-13 legacy API
  and crashes modern TVTopShelfContentProvider subclasses at launch.
- Main app writes per-section JSON snapshots (capped at 10 items each)
  to a shared App Group UserDefaults suite after bookmark, watch-history,
  and feed-cache changes, plus an initial write on launch.
- Enabled-sections list is mirrored to the same App Group so the
  extension can respect the user's selection without touching SwiftData.
- Settings → Top Shelf (tvOS only) lets the user toggle sections.
- Deep link playback shows a loading toast while video details are
  fetched, and an error toast if no source is configured.
2026-04-18 20:38:02 +02:00

33 lines
1.2 KiB
Swift

import Foundation
// Must stay in sync with Yattee/Core/Settings/TopShelfSection.swift.
enum TopShelfSection: String, Codable, CaseIterable, Sendable {
case continueWatching
case recentFeed
case recentBookmarks
var localizedTitle: String {
// The extension doesn't share the main app's string catalog, so we
// ship English fallbacks here. Keep in sync with
// Localizable.xcstrings entries of the same keys.
switch self {
case .continueWatching:
return String(localized: "home.section.continueWatching", defaultValue: "Continue Watching")
case .recentFeed:
return String(localized: "home.section.feed", defaultValue: "Feed")
case .recentBookmarks:
return String(localized: "home.section.bookmarks", defaultValue: "Bookmarks")
}
}
var snapshotKey: String {
switch self {
case .continueWatching: return "topShelf.continueWatching"
case .recentFeed: return "topShelf.recentFeed"
case .recentBookmarks: return "topShelf.recentBookmarks"
}
}
static let defaultOrder: [TopShelfSection] = [.continueWatching, .recentFeed, .recentBookmarks]
}