An unofficial Dumpert client for Apple TV, built with Swift 6.0 and SwiftUI.
Disclaimer
This project is not affiliated with, endorsed by, or associated with Dumpert or DPG Media B.V. Dumpert is a registered trademark of DPG Media B.V. All trademarks belong to their respective owners. This app consumes the public Dumpert API. Use at your own risk.
The easiest way to install DumpertTV on your Apple TV:
- Install TestFlight from the App Store on your Apple TV and iPhone
- Open the TestFlight invite link on your iPhone and accept the invite
- Open TestFlight on your Apple TV and install the app
- 9 tabs: Toppers, Nieuw, Reeten, VrijMiCo, Dashcam, Classics, Gekeken, Zoeken, Instellingen
- Hero banner with horizontally scrolling carousel and face-centered thumbnails
- Infinite scroll pagination on category and classics views
- Skeleton loading with shimmer animation while content loads
- Top Shelf extension showing trending content directly on the Apple TV home screen
- Immersive background with dynamic blurred imagery
- Loading screen with logo animation and random sound effect
- Sort order support for category tabs and search results
- Context menu on video cards (long press)
- Full-screen video playback via
AVPlayerViewController - Autoplay with configurable up-next overlay and countdown timer
- Next video preloading for seamless playback
- Playback speed control (0.5x, 0.75x, 1x, 1.25x, 1.5x, 2x)
- Watch progress tracking with throttled saves (5-second intervals)
- Resume overlay when returning to a previously watched video
- Top comment overlay showing popular comments during playback
- Now Playing info on the Lock Screen and Control Center
- Swipe gestures on the Siri Remote to skip to previous/next video
- Watched badge indicator on already-viewed content
- Dedicated Gekeken tab showing previously watched videos
- Track and manage watch history
- Watch Together via SharePlay (GroupActivities)
- Synchronized playback across multiple Apple TVs
- Participant count indicator
- Full-screen photo display with zoom controls
- Overlay with metadata (title, date, kudos)
- Full-text search with the Dumpert API
- Filters: media type, time period, minimum kudos, duration
- Sort order: relevance, date, kudos
- Popular tags and recent search suggestions
- In-memory result caching (5-minute TTL)
- Search history persistence
- CloudKit sync for watch progress, settings, curation entries, and search history across Apple TV devices
- Delta sync with change tokens for efficient updates
- Offline support with network monitoring banner
- ETag-based HTTP caching (304 Not Modified) for API responses
- Retry logic with exponential backoff (3 attempts, 2^n second delays) on 5xx and network errors
- Dutch (nl) and English (en) via String Catalogs
- All user-facing strings use
String(localized:comment:)for translator context
- VoiceOver labels throughout all views
- Adjustable action on hero carousel for screen reader users
- URL scheme:
dumpert://video/{id} - Used by the Top Shelf extension to open videos directly
| Toppers | Search |
|---|---|
![]() |
![]() |
| Reeten | VrijMiCo |
![]() |
![]() |
| Classics | Settings |
![]() |
![]() |
| Requirement | Version |
|---|---|
| Xcode | 26.3+ |
| tvOS deployment target | 18.0+ |
| Swift | 6.0 (strict concurrency) |
| XcodeGen | Latest |
| Apple Developer account | Required for CloudKit and code signing |
brew install xcodegengit clone https://github.com/rm335/dumpert.git
cd DumpertTVxcodegen generateThe
.xcodeprojis generated fromproject.ymlβ never edit it directly.
open Dumpert.xcodeproj- Select your development team for both the Dumpert and DumpertTopShelf targets.
- Change the bundle identifiers if needed (default:
nl.dumpert.tvos).
CloudKit sync is optional. If you want cross-device sync:
- Update
Dumpert/Dumpert.entitlementswith your own iCloud container identifier. - Update
DumpertTopShelf/DumpertTopShelf.entitlementswith your own app group. - Create the corresponding CloudKit container in the Apple Developer portal.
Without CloudKit, the app works fully with local-only persistence.
Build and run on an Apple TV or the tvOS Simulator.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β DumpertApp β
β (SwiftUI @main entry) β
β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β LoadingScreenView β ContentView (TabView, 9 tabs) β β
β β Toppers β Nieuw β Reeten β VrijMiCo β Dashcam β ... β β
β ββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββ β
β β β
β @Environment β
β β β
β ββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββ β
β β VideoRepository β β
β β @Observable @MainActor β β
β β Single source of truth β β
β βββββ¬βββββββββββ¬βββββββββββ¬βββββββββββ¬ββββββββββββββββββββ β
β β β β β β
β βββββΌββββ βββββΌβββββ βββββΌββββββββ ββΌβββββββββββββββ β
β β API β β Cache β β CloudKit β β NowPlaying / β β
β β Clientβ β Serviceβ β Service β β SharePlay β β
β β(actor)β β(actor) β β (actor) β β (@Observable) β β
β βββββββββ ββββββββββ βββββββββββββ βββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Pattern | Usage |
|---|---|
@Observable + @MainActor |
VideoRepository, NetworkMonitor, UserSettings, SharePlayService, ImmersiveBackgroundState β reactive state on main thread |
| Actor isolation | DumpertAPIClient, CacheService, CloudKitService, ImageCacheService β thread-safe services |
| Environment injection | VideoRepository, NetworkMonitor, ImmersiveBackgroundState, LoadingSoundPlayer injected via .environment() |
| Protocol-based DI | APIClientProtocol, CacheServiceProtocol for testability |
| Swift 6 strict concurrency | SWIFT_STRICT_CONCURRENCY: complete across all targets |
Dumpert API β DumpertItem (API model) β MediaItem (domain enum) β Video / Photo
β
VideoRepository
β
SwiftUI views via @Environment
dumpert/
βββ project.yml # XcodeGen project configuration
βββ Dumpert/ # Main app target
β βββ App/
β β βββ DumpertApp.swift # @main entry, environment setup, deep linking
β β βββ ContentView.swift # Root TabView with 9 tabs + offline banner
β βββ Models/
β β βββ API/ # Codable API response models
β β β βββ DumpertAPIResponse.swift
β β β βββ DumpertItem.swift
β β β βββ DumpertMedia.swift
β β β βββ DumpertStats.swift
β β β βββ DumpertComment.swift
β β βββ Domain/ # App domain models
β β βββ Video.swift
β β βββ Photo.swift
β β βββ MediaItem.swift # enum: .video(Video) | .photo(Photo)
β β βββ VideoCategory.swift
β β βββ UserSettings.swift
β β βββ WatchProgress.swift
β β βββ SearchFilter.swift
β β βββ SortOrder.swift
β β βββ CurationEntry.swift
β β βββ SearchHistoryEntry.swift
β β βββ WatchTogetherActivity.swift # GroupActivities for SharePlay
β βββ Networking/
β β βββ DumpertAPIClient.swift # Actor with ETag + retry
β β βββ APIClientProtocol.swift # Protocol for mocking
β β βββ APIEndpoint.swift # URL routing
β β βββ APIError.swift # Error types
β βββ Services/
β β βββ VideoRepository.swift # @Observable source of truth
β β βββ CacheService.swift # Disk cache (50MB LRU)
β β βββ CacheServiceProtocol.swift
β β βββ CloudKitService.swift # iCloud delta sync
β β βββ CategoryService.swift # Category filtering
β β βββ ImageCacheService.swift # Two-layer image cache (80MB mem + 200MB disk)
β β βββ ImagePrefetchService.swift
β β βββ NetworkMonitor.swift # NWPathMonitor connectivity
β β βββ FaceDetectionService.swift
β β βββ RefreshScheduler.swift
β β βββ SharePlayService.swift # GroupActivities coordination
β β βββ NowPlayingService.swift # MPNowPlayingInfoCenter + remote commands
β β βββ LoadingSoundPlayer.swift
β β βββ ImmersiveBackgroundState.swift
β β βββ ThumbnailUpgradeService.swift
β β βββ ThumbnailUpgradeDiskCache.swift
β βββ ViewModels/
β β βββ VideoPlayerViewModel.swift
β β βββ SearchViewModel.swift
β βββ Views/
β β βββ Components/ # Reusable UI components
β β β βββ VideoCardView.swift
β β β βββ VideoPreviewView.swift
β β β βββ VideoContextMenu.swift
β β β βββ FaceCenteredThumbnailView.swift
β β β βββ ImmersiveBackgroundView.swift
β β β βββ SectionTitleView.swift
β β β βββ KudosBadgeView.swift
β β β βββ WatchedBadgeView.swift
β β β βββ EmptyStateView.swift
β β β βββ SkeletonView.swift
β β β βββ ToastView.swift
β β β βββ AutoDismissModifier.swift
β β βββ LoadingScreen/
β β β βββ LoadingScreenView.swift # Netflix-style loading with logo animation
β β βββ Player/
β β β βββ VideoPlayerView.swift
β β β βββ UpNextOverlayView.swift
β β β βββ ResumeOverlayView.swift
β β β βββ TopCommentOverlayView.swift
β β β βββ NowPlayingOverlayView.swift
β β β βββ SharePlayIndicatorView.swift
β β β βββ FullScreenImageView.swift
β β β βββ FullScreenImageOverlay.swift
β β β βββ ZoomControlsView.swift
β β βββ Search/
β β β βββ SearchView.swift
β β β βββ SearchSuggestionsView.swift
β β β βββ SearchFilterBar.swift
β β βββ Sections/
β β β βββ ToppersSectionView.swift
β β β βββ CategorySectionView.swift
β β β βββ ClassicsSectionView.swift
β β β βββ WatchedSectionView.swift
β β βββ Settings/
β β βββ SettingsView.swift
β β βββ SettingsComponents.swift
β β βββ SettingsPickerDestination.swift
β β βββ UpNextSettingsView.swift
β βββ Extensions/
β β βββ String+HTML.swift # HTML tag/entity stripping
β β βββ Color+Dumpert.swift # Brand colors (#65B32E)
β β βββ Date+Formatting.swift
β βββ Utilities/
β β βββ AppLogger.swift # os.Logger categories
β β βββ DurationFormatter.swift # MM:SS formatting
β β βββ MediaItem+Present.swift
β βββ Assets.xcassets/
β βββ Dumpert.entitlements
β βββ Info.plist
βββ DumpertTopShelf/ # Top Shelf extension
β βββ ContentProvider.swift # TVTopShelfContentProvider
β βββ DumpertTopShelf.entitlements
β βββ Info.plist
βββ Shared/ # Shared between app + extension
β βββ TopShelfItem.swift
β βββ TopShelfDataStore.swift # App Group UserDefaults
β βββ TopShelfFetcher.swift
βββ DumpertTests/ # Unit tests (55 tests, 7 suites)
β βββ ModelTests.swift
β βββ APIDecodingTests.swift
β βββ DurationFormatterTests.swift
β βββ SearchFilterTests.swift
β βββ CacheServiceTests.swift
β βββ ErrorCaseTests.swift
β βββ AutoNextPlayTests.swift
β βββ Fixtures/ # JSON test fixtures
β βββ hotshiz.json
β βββ latest.json
β βββ search_reeten.json
β βββ foto_item.json
βββ LICENSE
The app uses the public Dumpert mobile API.
| Endpoint | Description |
|---|---|
GET /hotshiz |
Currently trending items |
GET /top5/week/{date} |
Top items of the week |
GET /top5/maand/{date} |
Top items of the month |
GET /latest/{page} |
Latest items (paginated) |
GET /search/{query}/{page}?order= |
Search results (paginated, optional sort order) |
GET /info/{id} |
Single item details |
GET /classics/{page} |
Classic items (paginated) |
GET /related/{id} |
Related items for a given video |
Base URL: https://post.dumpert.nl/api/v1.0
The project has 3 targets, defined in project.yml:
| Target | Type | Bundle ID | Description |
|---|---|---|---|
| Dumpert | tvOS Application | nl.dumpert.tvos |
Main app |
| DumpertTopShelf | App Extension | nl.dumpert.tvos.topshelf |
Top Shelf content provider |
| DumpertTests | Unit Test Bundle | nl.dumpert.tvos.tests |
55 tests across 7 suites |
55 tests across 7 suites, using Swift Testing framework:
| Suite | Tests | What it covers |
|---|---|---|
| ModelTests | 9 | WatchProgress, CurationEntry, UserSettings, VideoCategory, HTML stripping |
| APIDecodingTests | 12 | API response decoding, Video conversion, HLS preference, tags parsing |
| DurationFormatterTests | 6 | Time formatting (MM:SS, edge cases) |
| SearchFilterTests | 5 | Filter activation for media type, period, kudos, duration |
| CacheServiceTests | 4 | Persistence of watch progress, settings, curation, search history |
| ErrorCaseTests | 5 | API error descriptions, network/decoding/HTTP error handling, 5xx retry |
| AutoNextPlayTests | 14 | Playlist navigation, autoplay state, skip/previous, up-next overlay |
# Generate project and run tests
xcodegen generate && xcodebuild test \
-scheme Dumpert \
-destination 'platform=tvOS Simulator,name=Apple TV' \
-resultBundlePath TestResults| Technology | Usage |
|---|---|
| Swift 6.0 | Strict concurrency (complete mode) |
| SwiftUI | All UI, tvOS-native |
| AVKit | Video playback via AVPlayerViewController |
| GroupActivities | SharePlay / Watch Together |
| MediaPlayer | Now Playing info + remote command handling |
| CloudKit | Cross-device sync (private database, custom zone) |
| Network.framework | NWPathMonitor for connectivity |
| Vision.framework | Face detection for thumbnail centering |
| os.log | Structured logging (.cloudKit, .cache, .network) |
| String Catalogs | Localization (Dutch + English) |
| XcodeGen | Project generation from project.yml |
| Swift Testing | Unit test framework |
The Settings tab allows users to configure:
Weergave & content:
- Minimum kudos filter (0β500+)
- NSFW content toggle
- Negative kudos toggle
- Hide watched content
- Smart thumbnails (automatic thumbnail upgrade)
- Tile size (small, normal, large)
Afspelen:
- Autoplay on/off
- Video preview on focus
- Up-next overlay, countdown, and minimum video length
- Top comment overlay mode (off, single, all) with reading speed
- Swipe-to-skip on Siri Remote
- Resume overlay
- Minimum Reeten duration filter
Data & opslag:
- Manual refresh
- Clear cache, watch history, search history
- Reset to defaults
Settings are persisted locally and synced via CloudKit.
| Entitlement | Target | Purpose |
|---|---|---|
| iCloud containers | Dumpert | CloudKit sync |
| CloudKit | Dumpert | iCloud database access |
| KV store | Dumpert | Key-value sync |
| App Groups | Both | Share data between app and Top Shelf extension |
Contributions are welcome! Here's how:
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Install XcodeGen:
brew install xcodegen - Generate the project:
xcodegen generate - Make your changes
- Run the tests to make sure everything passes
- Commit your changes with a clear message
- Push to your fork and open a Pull Request
- Run
xcodegen generateafter changingproject.yml - Never commit
Dumpert.xcodeprojchanges directly β editproject.ymlinstead - Maintain Swift 6 strict concurrency compliance
- Add tests for new functionality
- Use actors for new services,
@Observable @MainActorfor new state holders - Follow existing patterns for file organization
This project is licensed under the MIT License β see the LICENSE file for details.







