RepoPilotOpen in app →

jellyfin/Swiftfin

Native Jellyfin Client for iOS and tvOS

Healthy

Healthy across the board

Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 1d ago
  • 33+ active contributors
  • Distributed ownership (top contributor 24% of recent commits)
Show 3 more →
  • MPL-2.0 licensed
  • CI configured
  • Tests present

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/jellyfin/swiftfin)](https://repopilot.app/r/jellyfin/swiftfin)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/jellyfin/swiftfin on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: jellyfin/Swiftfin

Generated by RepoPilot · 2026-05-10 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/jellyfin/Swiftfin shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across the board

  • Last commit 1d ago
  • 33+ active contributors
  • Distributed ownership (top contributor 24% of recent commits)
  • MPL-2.0 licensed
  • CI configured
  • Tests present

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live jellyfin/Swiftfin repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/jellyfin/Swiftfin.

What it runs against: a local clone of jellyfin/Swiftfin — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in jellyfin/Swiftfin | Confirms the artifact applies here, not a fork | | 2 | License is still MPL-2.0 | Catches relicense before you depend on it | | 3 | Default branch main exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 31 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>jellyfin/Swiftfin</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of jellyfin/Swiftfin. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/jellyfin/Swiftfin.git
#   cd Swiftfin
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of jellyfin/Swiftfin and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "jellyfin/Swiftfin(\\.git)?\\b" \\
  && ok "origin remote is jellyfin/Swiftfin" \\
  || miss "origin remote is not jellyfin/Swiftfin (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MPL-2\\.0)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MPL-2\\.0\"" package.json 2>/dev/null) \\
  && ok "license is MPL-2.0" \\
  || miss "license drift — was MPL-2.0 at generation time"

# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
  && ok "default branch main exists" \\
  || miss "default branch main no longer exists"

# 4. Critical files exist
test -f "Shared/AppIcons/AppIcons.swift" \\
  && ok "Shared/AppIcons/AppIcons.swift" \\
  || miss "missing critical file: Shared/AppIcons/AppIcons.swift"
test -f "Shared/Components/ImageView.swift" \\
  && ok "Shared/Components/ImageView.swift" \\
  || miss "missing critical file: Shared/Components/ImageView.swift"
test -f "PreferencesView/Sources/PreferencesView/PreferencesView.swift" \\
  && ok "PreferencesView/Sources/PreferencesView/PreferencesView.swift" \\
  || miss "missing critical file: PreferencesView/Sources/PreferencesView/PreferencesView.swift"
test -f ".github/workflows/ci.yml" \\
  && ok ".github/workflows/ci.yml" \\
  || miss "missing critical file: .github/workflows/ci.yml"
test -f "Documentation/libraries.md" \\
  && ok "Documentation/libraries.md" \\
  || miss "missing critical file: Documentation/libraries.md"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 31 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~1d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/jellyfin/Swiftfin"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

Swiftfin is a native iOS (16+) and tvOS (17+) video client for the Jellyfin media server, built entirely in Swift with VLC integration for direct-play capability. It provides a native, high-performance interface for streaming and managing media across Apple devices while maintaining bitstream-compatible playback to minimize transcoding overhead. Single-target Swift/SwiftUI monorepo with a modular library structure: PreferencesView/ is a standalone Swift Package (Package.swift, Sources/PreferencesView/) providing reusable preference UI components (Box.swift, KeyCommandsBuilder.swift, ViewExtensions.swift), while the main app integrates Jellyfin API client, VLC playback (via Cartfile), and platform-specific code (UIViewController+Swizzling.swift handles tvOS/iOS differences). Xcode workspace coordinates these components via .swiftpm/xcode/package.xcworkspace/.

👥Who it's for

iOS and tvOS users who self-host or access Jellyfin media servers and want a native Apple client experience; Swift developers contributing to the open-source project who understand SwiftUI and Jellyfin's REST API.

🌱Maturity & risk

Actively developed and production-ready: the project has iOS and tvOS TestFlight builds, is published on the Apple App Store (id 1604098728), maintains CI/CD workflows in .github/workflows/ (ci.yml, testflight.yml, validate-pr.yaml), and requires minimum iOS 16+ / tvOS 17+ with Jellyfin 10.11+ server compatibility. Regular releases indicate ongoing maintenance and active user base.

Low-to-moderate risk for core features, but tvOS lacks TestFlight updates per discussion #1294, so tvOS features may lag behind iOS. The project depends on external frameworks (VLC via Cartfile, ChromeCastFramework.json) and custom Jellyfin API integration, requiring server version coordination. Single repository structure suggests consolidated codebase, but no visible monorepo guardrails documented in README.

Active areas of work

Active release management (see .github/release.yml) and pull-request validation workflows (validate-pr.yaml); TestFlight deployment for iOS is automated (testflight.yml); code style enforced via .swiftformat and .swiftlint.yml. Internationalization is ongoing via Weblate (translation badge in README), and documentation is being expanded (Documentation/libraries.md, players.md, version.md on media support and player feature parity).

🚀Get running

git clone https://github.com/jellyfin/Swiftfin.git
cd Swiftfin
# Install dependencies via Carthage (Cartfile present)
carthage update --platform iOS,tvOS
# Open in Xcode
open Swiftfin.xcworkspace
# For Ruby tooling (linting, formatting):
bundle install

Then build via Xcode (⌘B) or xcodebuild build.

Daily commands: Open Swiftfin.xcworkspace in Xcode 15+, select iOS or tvOS scheme, and press ⌘R to build & run on simulator or device. For CI validation: bundle exec fastlane (inferred from Gemfile/Ruby presence); format check via .swiftformat and .swiftlint.yml rules.

🗺️Map of the codebase

  • Shared/AppIcons/AppIcons.swift — Central app icon management for all theme variants (Dark, Light, Inverted, Primary); essential for understanding UI theming patterns.
  • Shared/Components/ImageView.swift — Core image rendering component used throughout the app; understanding this is critical for media display and performance optimization.
  • PreferencesView/Sources/PreferencesView/PreferencesView.swift — Main preferences/settings framework used across iOS and tvOS; foundational for understanding user configuration patterns.
  • .github/workflows/ci.yml — Continuous integration pipeline definition; essential for understanding build, test, and deployment processes.
  • Documentation/libraries.md — Documents all third-party dependencies and their integration rationale; critical for understanding the dependency graph and available capabilities.
  • Shared/Components/BlurView.swift — Platform-specific blur effects abstraction for iOS/tvOS; demonstrates the native visual approach mentioned in the README.
  • .swiftformat — Code style and formatting conventions enforced across the codebase; necessary for maintaining consistency in contributions.

🛠️How to make changes

Add a New Reusable UI Component

  1. Create new Swift file in Shared/Components/ directory (Shared/Components/YourComponentName.swift)
  2. Import SwiftUI and follow the existing component patterns (e.g., BlurView.swift, ImageView.swift for reference) (Shared/Components/YourComponentName.swift)
  3. If component needs platform-specific behavior, add conditional compilation blocks or follow PreferencesView UIViewController+Swizzling.swift approach for bridging UIKit (Shared/Components/YourComponentName.swift)
  4. Export component from Shared module and import in feature layers as needed (Shared/Components/YourComponentName.swift)

Add New App Icon Theme Variant

  1. Create SVG variant in Resources/AppIcons/{ThemeName}/ directory (follow naming pattern of existing Dark/Light/Inverted variants) (Resources/AppIcons/{ThemeName}/AppIcon-{themeName}.svg)
  2. Create corresponding Swift icon class following pattern from DarkAppIcon.swift or LightAppIcon.swift (Shared/AppIcons/{ThemeName}AppIcon.swift)
  3. Register new icon variant in the central AppIcons.swift registry (Shared/AppIcons/AppIcons.swift)
  4. Update app configuration to make theme selectable in PreferencesView (PreferencesView/Sources/PreferencesView/PreferencesView.swift)

Add Support for New Device Configuration or Preference

  1. Define preference component using PreferencesView framework patterns (check PreferencesView.swift for layout examples) (PreferencesView/Sources/PreferencesView/PreferencesView.swift)
  2. If adding tvOS-specific keyboard commands, extend KeyCommandsBuilder.swift; for remote buttons, extend PressCommandBuilder.swift (PreferencesView/Sources/PreferencesView/KeyCommandsBuilder.swift)
  3. Add localization strings following the Scripts/Translations/ pattern; ensure key naming matches existing conventions (Scripts/Translations/AlphabetizeStrings.swift)
  4. If device-specific, use CompactOrRegularView.swift or AlternateLayoutView.swift to provide form-factor-specific UI in preferences (Shared/Components/CompactOrRegularView.swift)

Add New Localization/Language Support

  1. Prepare translation strings and place in appropriate localization bundle (Shared/Components/Localization/CulturePicker.swift)
  2. Update CulturePicker.swift to include new language option in the culture selection list (Shared/Components/Localization/CulturePicker.swift)
  3. Run the translation cleanup utility to verify no unused keys exist (Scripts/Translations/FindUnusedStrings.swift)
  4. Update Documentation/version.md or README if this is a significant language addition (README.md)

🔧Why these technologies

  • SwiftUI — Enables declarative, platform-agnostic UI that compiles natively for iOS 16+ and tvOS 17+, ensuring native look and feel across device classes.
  • VLC Framework — Maximizes direct play support with broad codec compatibility; significantly reduces transcoding overhead compared to AVPlayer-only approach.
  • Carthage — Decentralized dependency management; allows fine-grained control over framework versions without Xcode project lock-in.
  • Swift Package Manager (PreferencesView) — Modular preferences

🪤Traps & gotchas

  1. tvOS has no TestFlight: TestFlight builds are iOS-only; tvOS testing happens via manual device provisioning or App Store review per discussion #1294. 2. Carthage binary frameworks: Cartfile specifies VLC and ChromeCast versions; mismatches or outdated cache (~/Library/Caches/Carthage) cause opaque build failures. 3. Swift Package version pins: PreferencesView/Package.resolved locks transitive dependencies; running swift package update outside Xcode context may break builds. 4. Jellyfin server minimum version: Requires Jellyfin 10.11+; older servers may lack required API endpoints. 5. Code style enforcement is strict: PR validation (validate-pr.yaml) runs SwiftLint and SwiftFormat; non-conforming code blocks merges. 6. No visible test directory: Test coverage not obvious from file list; contribute accordingly.

🏗️Architecture

💡Concepts to learn

  • Direct Play / Bitstream-Compatible Playback — Swiftfin's core selling point is avoiding server-side transcoding by using VLC's native codec support; understanding when and why transcode fallback occurs is essential for player feature debugging.
  • SwiftUI Preference Keys & Environment — PreferencesView makes heavy use of SwiftUI's PreferenceKeys.swift and custom environment modifiers; mastering this pattern is required to add new preference UI components.
  • Method Swizzling (UIViewController+Swizzling.swift) — This Objective-C runtime technique bridges tvOS/iOS view controller differences at runtime; understanding swizzling risks (name collisions, fragility) is critical for platform-specific hacks.
  • Carthage Binary Framework Dependency Management — Swiftfin uses Cartfile (not CocoaPods or SPM) for VLC and ChromeCast; Carthage's binary caching and platform-specific builds create subtle failures if not understood.
  • Jellyfin REST API Authentication & Session Management — The app must authenticate with Jellyfin servers via token-based auth and manage streaming sessions; API versioning (10.11+ requirement) means compatibility layers are needed.
  • Swift Package Manager (SPM) Modular Targets — PreferencesView is a standalone SPM with separate Package.swift; understanding how SPM resolves dependencies and integrates into Xcode workspaces is required for adding new modular features.
  • CI/CD via GitHub Actions + Fastlane — Workflows in .github/workflows/ automate linting, validation, and TestFlight uploads; understanding this pipeline is essential for PR submission and troubleshooting CI failures.
  • jellyfin/jellyfin — The backend media server that Swiftfin connects to; essential for understanding API contracts and server compatibility requirements.
  • jellyfin/jellyfin-mobile — Alternative Jellyfin client for mobile platforms (React Native); provides comparative approach to multi-platform Jellyfin client development.
  • video-dev/VLC-iOS — VLC media player framework for iOS/tvOS; Swiftfin integrates VLC via Carthage for native playback capability.
  • jellyfin/jellyfin-web — Web-based Jellyfin client; shares API design patterns and server feature parity expectations with Swiftfin.
  • signalapp/Signal-iOS — Production Swift/SwiftUI codebase with mature tvOS/iOS multi-platform patterns; reference for platform-specific bridging and modular architecture similar to Swiftfin's approach.

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive unit tests for PreferencesView package

The PreferencesView package (PreferencesView/Sources/PreferencesView/) contains several key components like KeyCommandsBuilder, PressCommandBuilder, and UIViewController+Swizzling but has no visible test suite. This is critical for a UI framework component that other parts of the app depend on. Adding tests would prevent regressions and improve code quality.

  • [ ] Create PreferencesView/Tests directory structure matching Swift Package Manager conventions
  • [ ] Add unit tests for KeyCommandsBuilder.swift covering command chain building and action handling
  • [ ] Add unit tests for PressCommandBuilder.swift with various press configurations
  • [ ] Add integration tests for UIViewController+Swizzling.swift to verify method swizzling doesn't break view lifecycle
  • [ ] Add tests for PreferenceKeys.swift to validate custom preference key behavior
  • [ ] Integrate test target into PreferencesView/Package.swift and CI pipeline (ci.yml)

Create platform-specific CI workflow for tvOS native testing

The repo targets both iOS 16+ and tvOS 17+ according to README, but the workflows (ci.yml, testflight.yml, validate-pr.yaml) don't show explicit tvOS device/simulator testing steps. tvOS has significant UI/navigation differences (remote control, focus engine) that require dedicated testing to catch platform-specific bugs early.

  • [ ] Review existing ci.yml workflow to identify iOS-only test configurations
  • [ ] Add tvOS simulator build and test job in ci.yml using xcodebuild with -sdk appletvsimulator
  • [ ] Configure tvOS-specific test targets to exercise remote control input handling and focus navigation
  • [ ] Add separate validation step for tvOS in validate-pr.yaml to gate PRs
  • [ ] Document tvOS testing requirements in Documentation/contributing.md

Extract and document ChromeCast/playback integration architecture

The repo has ChromeCastFramework.json at root level but no corresponding documentation explaining how external playback frameworks (ChromeCast, AirPlay, VLC) are integrated. This is a complex architectural concern for new contributors. Documentation/players.md exists but doesn't explain the integration patterns or extension points.

  • [ ] Review ChromeCastFramework.json and identify where ChromeCast/external player logic is implemented in the codebase
  • [ ] Expand Documentation/players.md with a section explaining the playback architecture (VLC integration, external framework bindings, protocol separation)
  • [ ] Create a diagram or ASCII flowchart showing how playback routing works between native playback, VLC, ChromeCast, and other backends
  • [ ] Add code examples showing how to add a new external player type (reference existing implementations)
  • [ ] Document the extension points and interfaces that external players must conform to

🌿Good first issues

  • Add unit tests for PreferencesView/Sources/PreferencesView/Box.swift and ViewExtensions.swift — these foundational UI components lack visible test coverage, improving test suite robustness.
  • Document the VLC integration and direct-play bitstream handling in Documentation/ with a new 'Architecture.md' file — currently not explained, making onboarding harder for contributors who need to modify playback behavior.
  • Improve Cartfile documentation in README.md: add a troubleshooting section for common Carthage cache issues (e.g., 'if build fails, run rm -rf ~/Library/Caches/Carthage') — many contributors hit this.
  • Add i18n contribution guide to Documentation/contributing.md with a step-by-step Weblate workflow — translation badge is present but contributing flow is unclear.
  • Create a tvOS-specific testing guide in Documentation/ explaining manual device provisioning since TestFlight is unavailable — currently undocumented, blocking tvOS contributor setup.

Top contributors

Click to expand

📝Recent commits

Click to expand
  • f4478fa — Translated using Weblate (French) (antvgr)
  • f09c584 — Translated using Weblate (Arabic) (Translation expert)
  • 5613a77 — Translated using Weblate (Japanese) (Mia)
  • e8d2017 — Bump SDK to 1.0.0 (#2009) (JPKribs)
  • 7edcf99 — Translated using Weblate (Japanese) (Atsuki Miyazaki)
  • cb8a8eb — Translated using Weblate (Japanese) (musenq)
  • b553e99 — Translated using Weblate (Japanese) (Atsuki Miyazaki)
  • f099570 — Translated using Weblate (Japanese) (Atsuki Miyazaki)
  • dd45f43 — Translated using Weblate (Japanese) (musenq)
  • 98a296b — Translated using Weblate (German) (KilianSander)

🔒Security observations

The Swiftfin iOS/tvOS Jellyfin client codebase shows moderate security posture. Primary concerns include: (1) Use of Objective-C runtime method swizzling which can bypass security controls, (2) Missing explicit network security configuration verification, (3) Unaudited external dependencies (especially ChromeCast framework), and (4) Lack of security policy documentation. The project structure suggests good development practices with CI/CD pipelines and code linting, but security-specific hardening is not apparent from the provided file structure. Most vulnerabilities are preventable through proper configuration, dependency management, and security documentation. No critical hardcoded credentials or obvious injection vulnerabilities were detected from the file structure alone.

  • Medium · Potential Runtime Method Swizzling Security Risk — PreferencesView/Sources/PreferencesView/UIViewController+Swizzling.swift. The file 'UIViewController+Swizzling.swift' in PreferencesView indicates use of Objective-C runtime method swizzling. This technique can bypass security controls, interfere with system frameworks, and make code difficult to audit. It may also conflict with Apple's security measures and future iOS/tvOS updates. Fix: Review the swizzling implementation and replace with standard iOS/tvOS APIs where possible. If swizzling is necessary, document the purpose, minimize scope, and add comprehensive tests. Consider alternative approaches using delegation or composition patterns.
  • Medium · Missing Security Headers and Network Configuration Review — App configuration (Info.plist or equivalent). No visible security configuration files (e.g., App Transport Security, network security configuration) in the provided file structure. The app communicates with Jellyfin servers, and network security should be explicitly configured to prevent man-in-the-middle attacks. Fix: Ensure proper App Transport Security (ATS) configuration is in place. Disable ATS only for debugging/localhost. Implement certificate pinning for Jellyfin server connections. Validate all HTTPS certificates and use secure protocols only.
  • Medium · Insufficient Dependency Vulnerability Information — Cartfile, PreferencesView/Package.swift, ChromeCastFramework.json. The Cartfile and Package.swift files are referenced but their contents are not provided. External dependencies could contain known vulnerabilities. The Chromium/ChromeCast framework (ChromeCastFramework.json) is particularly sensitive for security. Fix: Review all dependencies for known CVEs using tools like OWASP Dependency-Check or Snyk. Keep dependencies updated to the latest secure versions. Audit ChromeCast framework integration for proper certificate validation. Implement automated dependency scanning in CI/CD pipeline.
  • Low · Test Flight Distribution Configuration Not Audited — .github/workflows/testflight.yml. The TestFlight workflow (.github/workflows/testflight.yml) is present, suggesting pre-release distribution. Without reviewing the actual workflow content, potential risks include exposed credentials in CI/CD environment variables or improper code signing. Fix: Ensure CI/CD secrets are properly managed (use GitHub Secrets for credentials, API keys). Implement branch protection rules requiring code review before TestFlight builds. Use short-lived credentials and automatic secret rotation where possible.
  • Low · Missing Security Documentation — Repository root. No visible SECURITY.md or security policy file in the repository root. This makes it difficult for security researchers to report vulnerabilities responsibly. Fix: Create a SECURITY.md file following GitHub's security policy template. Include responsible disclosure guidelines, contact information for security issues, and expected response timeframes.
  • Low · Potential Hardcoded Configuration Values — .swiftformat, .swiftlint.yml, Brewfile. Multiple configuration files present (.swiftformat, .swiftlint.yml, etc.) without visibility into their contents. These could potentially contain hardcoded values, API endpoints, or other sensitive configuration. Fix: Audit all configuration files for hardcoded sensitive values. Move API endpoints and configuration to environment variables or secure configuration management. Ensure no credentials are committed to version control.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · jellyfin/Swiftfin — RepoPilot