analogcode/Swift-Radio-Pro
Professional Radio Station App for iOS!
Solo project — review before adopting
worst of 4 axessingle-maintainer (no co-maintainers visible); no tests detected
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 5w ago
- ✓MIT licensed
- ✓CI configured
Show 2 more →Show less
- ⚠Solo or near-solo (1 contributor active in recent commits)
- ⚠No test directory detected
What would change the summary?
- →Use as dependency Mixed → Healthy if: onboard a second core maintainer
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 "Forkable" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/analogcode/swift-radio-pro)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/analogcode/swift-radio-pro on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: analogcode/Swift-Radio-Pro
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:
- 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. - 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.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/analogcode/Swift-Radio-Pro 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
WAIT — Solo project — review before adopting
- Last commit 5w ago
- MIT licensed
- CI configured
- ⚠ Solo or near-solo (1 contributor active in recent commits)
- ⚠ No test directory detected
<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 analogcode/Swift-Radio-Pro
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/analogcode/Swift-Radio-Pro.
What it runs against: a local clone of analogcode/Swift-Radio-Pro — 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 analogcode/Swift-Radio-Pro | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 65 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of analogcode/Swift-Radio-Pro. If you don't
# have one yet, run these first:
#
# git clone https://github.com/analogcode/Swift-Radio-Pro.git
# cd Swift-Radio-Pro
#
# 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 analogcode/Swift-Radio-Pro and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "analogcode/Swift-Radio-Pro(\\.git)?\\b" \\
&& ok "origin remote is analogcode/Swift-Radio-Pro" \\
|| miss "origin remote is not analogcode/Swift-Radio-Pro (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "SwiftRadio/App/AppDelegate.swift" \\
&& ok "SwiftRadio/App/AppDelegate.swift" \\
|| miss "missing critical file: SwiftRadio/App/AppDelegate.swift"
test -f "SwiftRadio/Coordinators/MainCoordinator.swift" \\
&& ok "SwiftRadio/Coordinators/MainCoordinator.swift" \\
|| miss "missing critical file: SwiftRadio/Coordinators/MainCoordinator.swift"
test -f "SwiftRadio/Model/RadioStation.swift" \\
&& ok "SwiftRadio/Model/RadioStation.swift" \\
|| miss "missing critical file: SwiftRadio/Model/RadioStation.swift"
test -f "SwiftRadio/Services/AudioSetupService.swift" \\
&& ok "SwiftRadio/Services/AudioSetupService.swift" \\
|| miss "missing critical file: SwiftRadio/Services/AudioSetupService.swift"
test -f "SwiftRadio/Networking/NetworkService.swift" \\
&& ok "SwiftRadio/Networking/NetworkService.swift" \\
|| miss "missing critical file: SwiftRadio/Networking/NetworkService.swift"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 65 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~35d)"
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/analogcode/Swift-Radio-Pro"
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).
⚡TL;DR
Swift Radio Pro is a production-ready iOS radio streaming app built entirely in Swift that demonstrates how to build a multi-station radio player with CarPlay support, background audio playback, and metadata integration. It uses FRadioPlayer for streaming and iTunes API for album art, supporting both local JSON station lists and remote configuration—used as the foundation for 80+ apps on the App Store. Single-app structure: SwiftRadio/ contains App (AppDelegate, SceneDelegate), CarPlay scene delegate, ViewControllers/Coordinators for navigation, Config/ for customization, Data/ for stations.json, Cells/ for table views, and Helpers/ for utilities. SwiftRadio.xcodeproj manages all build configuration. No separate modules—everything is one target with entry points for iOS and CarPlay via SceneDelegate routing.
👥Who it's for
iOS developers building radio or audio streaming apps who need a reference implementation with CarPlay, lock screen controls, and station management already solved; also serves as a template for App Store apps requiring professional audio playback with minimal custom streaming code.
🌱Maturity & risk
This is production-ready and actively maintained: it powers 80+ shipping App Store apps, has CI/CD workflows for both iOS and CarPlay builds, and uses modern Swift Package Manager dependencies. The codebase shows professional polish with Coordinator pattern navigation, proper asset management, and localization infrastructure via Xcode String Catalog.
Low risk for production use, but dependency on FRadioPlayer (a single external streaming library) means audio quality and format support issues flow through all 80+ derived apps. The repo has relatively few commits visible in the structure provided, so verify recent maintenance on the dev branch. Single-maintainer burden is typical for open-source templates but acceptable given stable, focused scope.
Active areas of work
The repo maintains active CI/CD with separate workflows for iOS and CarPlay (see .github/workflows/). Recent work appears focused on CarPlay support (dedicated CarPlaySceneDelegate), string localization readiness (Localizable.xcstrings), and configuration flexibility (Config.swift for customization without code changes).
🚀Get running
git clone https://github.com/analogcode/Swift-Radio-Pro.git
cd Swift-Radio-Pro
open SwiftRadio.xcodeproj
Then edit SwiftRadio/Config/Config.swift to set your stations URL, email, and app settings. Xcode will resolve SPM dependencies automatically (FRadioPlayer, LNPopupController, NVActivityIndicatorView, MarqueeLabel).
Daily commands: Open SwiftRadio.xcodeproj in Xcode 15+. Select the SwiftRadio scheme (iOS) or the CarPlay scheme. Press Cmd+R to build and run on simulator or device. The app loads stations from stations.json and plays the first tap.
🗺️Map of the codebase
SwiftRadio/App/AppDelegate.swift— Entry point for the iOS application; initializes app lifecycle, audio session configuration, and root coordinator setup.SwiftRadio/Coordinators/MainCoordinator.swift— Core navigation coordinator managing the station list, now-playing, and detail flows; essential for understanding UI architecture.SwiftRadio/Model/RadioStation.swift— Primary data model for radio stations; used throughout the app for streaming, display, and persistence.SwiftRadio/Services/AudioSetupService.swift— Configures audio session and playback behavior; critical for background audio, CarPlay, and lock screen integration.SwiftRadio/Networking/NetworkService.swift— Handles all network requests for station metadata and iTunes API calls; central point for external data fetching.SwiftRadio/Model/StationsManager.swift— Manages station loading from JSON files and caching; orchestrates station data lifecycle.SwiftRadio/Config/Config.swift— Centralized configuration constants (URLs, API keys, feature flags); referenced across the entire codebase.
🛠️How to make changes
Add a New Radio Station
- Edit SwiftRadio/Data/stations.json to add a new station object with name, streamURL, website, and imageURL (
SwiftRadio/Data/stations.json) - The StationsManager will automatically parse and load the new station on app startup (
SwiftRadio/Model/StationsManager.swift) - Optional: Add station artwork to SwiftRadio/Images.xcassets/Stations/ if using local images instead of URLs (
SwiftRadio/Images.xcassets/Stations)
Add a New Info Screen (About, Features, etc.)
- Create a new UIViewController subclass in SwiftRadio/ViewControllers/ (e.g., NewInfoViewController.swift) (
SwiftRadio/ViewControllers/AboutViewController.swift) - Add a new coordinator method in AboutCoordinator.swift to navigate to the new screen (
SwiftRadio/Coordinators/AboutCoordinator.swift) - Define the route trigger in Config.swift (if configuration-driven) or update MainCoordinator to wire the flow (
SwiftRadio/Config/Config.swift) - Wrap the new view controller in BottomSheetViewController if following the app's modal pattern (
SwiftRadio/ViewControllers/BottomSheetViewController.swift)
Customize Audio Playback or Lock Screen Behavior
- Open AudioSetupService.swift and modify configureAudioSession() for session category/mode or add new delegate handlers (
SwiftRadio/Services/AudioSetupService.swift) - Update NowPlayingViewController to set custom lock screen artwork or metadata via MPNowPlayingInfoCenter (
SwiftRadio/ViewControllers/NowPlayingViewController.swift) - If adding Handoff support, conform to Handoffable protocol and implement activity restoration logic (
SwiftRadio/Helpers/Handoffable.swift)
Integrate a Remote Station List
- Update the remote URL in Config.swift to point to your remote JSON endpoint (
SwiftRadio/Config/Config.swift) - Modify StationsManager.loadStations() to fetch from the remote URL using NetworkService.swift (
SwiftRadio/Model/StationsManager.swift) - Implement pull-to-refresh in the station list view controller to trigger reloads (
SwiftRadio/ViewControllers/NowPlayingViewController.swift) - Handle network errors gracefully; fall back to bundled stations.json if the remote call fails (
SwiftRadio/Networking/NetworkService.swift)
🔧Why these technologies
- SwiftUI / UIKit hybrid — Legacy UIKit codebase modernized for compatibility with iOS 12+; supports both traditional and newer UI paradigms
- Coordinator pattern — Decouples navigation logic from view controllers; enables testable, reusable flow management across iOS and CarPlay
- AVAudioEngine / AVAudioSession — Required for background audio playback, lock
🪤Traps & gotchas
Remote station URLs require HTTPS: Config.swift likely enforces secure streaming—plain HTTP URLs may fail silently. Image caching helpers: UIImage+Cache and UIImageView+Cache expect specific URL schemes (local asset names vs. full http URLs)—mixing formats can cause broken images. FRadioPlayer metadata parsing: iTunes API fallback for album art requires network; slow connections may show placeholder images during playback. CarPlay scene lifecycle: CarPlaySceneDelegate is separate from AppDelegate—changes to session management affect both entry points differently. Info.plist constraints: Background Modes (audio, remote-control) must be enabled in capabilities, not visible in file list but required.
🏗️Architecture
💡Concepts to learn
- AVAudioSession background modes — Swift Radio Pro plays audio while the app is backgrounded; understanding background audio session configuration (category .playback, options .deferredSpeakerInitialization) is non-obvious but critical for app store approval and correct behavior
- CarPlay scene delegation — This app supports both iOS and CarPlay via separate SceneDelegate entry points; understanding scene manifests and template-based UI (CPListTemplate, CPNowPlayingTemplate) is required to maintain the CarPlay feature
- Coordinator pattern (Navigation) — Swift Radio Pro uses Coordinators/ for decoupled view navigation instead of UIViewController segues; learning this pattern is essential for contributing features that span multiple screens
- HTTP streaming metadata (Icecast) — Radio streams often embed metadata (artist, track name) in HTTP headers or within the audio stream itself (Icecast format); FRadioPlayer abstracts this but understanding it helps debug missing metadata or format incompatibilities
- UIImage caching strategies — The app implements custom UIImage+Cache and UIImageView+Cache helpers to avoid reload ing station artwork from network; understanding memory vs. disk caching trade-offs prevents flickering and excessive data use
- MediaPlayer framework (Control Center integration) — Lock screen and Control Center playback controls require MediaPlayer framework command handlers; Swift Radio Pro integrates these via LNPopupController but understanding remote commands is needed for custom player UI
- Xcode String Catalog localization — Instead of .strings files, this app uses Localizable.xcstrings (Xcode 15+ feature) for managing translations; understanding this modern approach is necessary to add new languages or modify user-facing text
🔗Related repos
fethica/FRadioPlayer— The streaming and metadata parsing backbone of Swift Radio Pro; understanding FRadioPlayer's AVPlayer wrapping and HTTP metadata extraction is essential for debugging audio issuesfethica/Swift-Radio-Android— Sister project in Kotlin/Jetpack Compose using the same station.json format; useful for understanding cross-platform architecture decisions and feature parityLeoNatan/LNPopupController— Dependency for the now-playing popup bar—handles the slide-up player panel and integrates with iOS lock screen controlscbpowell/MarqueeLabel— Dependency for scrolling station name and track title labels in the UI—essential for readability when text is longer than available spaceninjaprox/NVActivityIndicatorView— Dependency providing the loading spinner animations shown during station buffering and metadata fetching
🪄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 Unit Tests for Core Coordinators (Coordinator.swift, MainCoordinator.swift, AboutCoordinator.swift)
The repo has two CI workflows (ios.yml, carplay.yml) but no visible test files. The coordinator pattern is central to navigation architecture. Adding unit tests for coordinator initialization, navigation flow, and lifecycle would catch regressions early and serve as documentation for the navigation model.
- [ ] Create SwiftRadioTests/ folder with CoordinatorTests.swift
- [ ] Add tests for Coordinator protocol compliance and MainCoordinator child/parent relationships
- [ ] Add tests for AboutCoordinator navigation flow
- [ ] Integrate test execution into .github/workflows/ios.yml using xcodebuild test
- [ ] Aim for >80% coverage of SwiftRadio/Coordinators/ directory
Add Unit Tests for Data Models and Parsing (stations.json, Content.swift, InfoSection.swift)
The app loads radio stations from a local JSON file (SwiftRadio/Data/stations.json) and has config models (Content.swift, InfoSection.swift) but no visible tests for JSON decoding or model validation. This is critical since malformed station data breaks the app. Tests would validate the JSON structure and ensure the Config system can handle edge cases.
- [ ] Create ConfigTests.swift and DataParsingTests.swift in test target
- [ ] Add tests to verify stations.json Decodable conformance and required fields (name, url, image, etc.)
- [ ] Add tests for Content and InfoSection model parsing from config
- [ ] Add tests for invalid/missing station fields to ensure graceful degradation
- [ ] Update ios.yml workflow to report test coverage for Data and Config directories
Add Integration Tests for Audio Playback and CarPlay Delegate in .github/workflows/carplay.yml
The repo has a separate carplay.yml workflow but it likely only builds; there are no visible integration tests validating that CarPlaySceneDelegate and AppDelegate properly initialize audio sessions, handle interruptions, and maintain playback state. Adding UI tests would catch CarPlay-specific regressions (e.g., scene delegation, remote commands).
- [ ] Create SwiftRadioUITests/ with CarPlaySceneTests.swift and AudioPlaybackTests.swift
- [ ] Add UI tests for AppDelegate initialization and audio session setup
- [ ] Add tests for CarPlaySceneDelegate lifecycle and MPMediaPlayer remote command handling
- [ ] Add tests verifying station streaming initiates without crashes
- [ ] Integrate UI test execution into carplay.yml workflow with -destination 'generic/platform=carOS'
🌿Good first issues
- Add unit tests for StationTableViewCell reuse identifier generation and UITableViewCell+reuseIdentifier helper—currently no test coverage visible and this is a common bug source in UIKit table views.
- Document the stations.json schema with a full example including optional fields (website, longDesc) and validation rules, plus add a schema validator script to prevent malformed station files from breaking the app.
- Implement a local image fallback system: if a remote station imageURL fails to load, try fetching the named asset as a fallback—currently broken images remain broken instead of gracefully degrading.
📝Recent commits
Click to expand
Recent commits
a45421c— Merge pull request #211 from analogcode/fix/ci-simulator-name (fethica)3e73583— Fix CI builds by using iPhone 17 Pro simulator directly (fethica)2fcd07a— Merge pull request #210 from analogcode/move-android-link-below-screenshots (fethica)606f71e— Replace em dash with period (fethica)cb02792— Move Android link below screenshots (fethica)a512b28— Merge pull request #209 from analogcode/add-android-link (fethica)6ec4ba9— Add link to Swift Radio Android in README (fethica)217a4a2— Merge pull request #206 from analogcode/dev (fethica)aa866d8— Update README.md (fethica)6a553db— Localize hardcoded About screen labels via String Catalog (fethica)
🔒Security observations
The Swift Radio Pro application demonstrates generally sound security practices for an iOS radio streaming app. The primary concerns are around network security (lack of certificate pinning and HTTPS enforcement documentation), and missing explicit data validation for remote configuration sources. The codebase lacks evidence of security hardening measures but contains no obvious critical vulnerabilities. Key recommendations include implementing certificate pinning, enforcing HTTPS via ATS configuration, validating all remote data inputs strictly, and establishing formal security documentation practices. The architecture appears straightforward without complex injection vectors, but network communication security should be enhanced given the app's reliance on streaming and remote metadata.
- Medium · Potential Insecure Network Communication —
SwiftRadio/Data/stations.json, SwiftRadio/Config/Content.swift. The application streams radio content from remote sources. Without visible HTTPS enforcement in the codebase, radio stream URLs may be vulnerable to man-in-the-middle attacks if HTTP is used. The stations.json file contains stream URLs that should be validated for secure transport. Fix: Ensure all stream URLs use HTTPS. Implement App Transport Security (ATS) configuration in Info.plist to enforce secure connections. Only allow HTTPS for domain exceptions where absolutely necessary, with documented justification. - Medium · Missing Code Signing and Certificate Pinning —
SwiftRadio (entire network layer). No evidence of certificate pinning implementation for remote API calls (e.g., iTunes API for metadata). This makes the app vulnerable to MITM attacks even with HTTPS if an attacker compromises a CA or uses a different valid certificate. Fix: Implement certificate pinning for critical API endpoints. Use TrustKit or similar libraries to validate server certificates against a known set of public keys. - Low · Potential Sensitive Data in User Interface State Files —
SwiftRadio.xcodeproj/project.xcworkspace/xcuserdata/. User-specific xcuserdata files are present in version control (.xcuserdata folders with UserInterfaceState.xcuserstate). While these typically contain UI state only, they may inadvertently contain sensitive debugging information or user preferences. Fix: Add xcuserdata/ to .gitignore to prevent accidental commits of user-specific build artifacts. Review .gitignore for completeness and ensure it covers all standard Xcode artifacts. - Low · Missing Security Headers Documentation —
SwiftRadio (documentation gap). No visible implementation of security best practices documentation for app-level security policies (e.g., content security, data validation strategies). This is a gap in security guidance for maintainers. Fix: Create a SECURITY.md file documenting security practices, vulnerability disclosure process, and security requirements for dependencies. - Low · Remote Configuration via JSON —
SwiftRadio/Data/stations.json, SwiftRadio/Config/Content.swift. The application uses stations.json for station data. If this configuration can be loaded from a remote source, there's a risk of remote code injection or malicious station data without proper validation. Fix: Implement strict validation for all remotely loaded JSON data. Use Codable with strict type checking. Consider code signing remote configuration or using signed certificates for remote updates. - Low · No Visible Dependency Vulnerability Scanning —
.github/workflows/ios.yml, .github/workflows/carplay.yml. The Package.resolved file suggests Swift Package Manager usage, but no automated dependency scanning (e.g., via GitHub's Dependabot) is evident in workflows. Fix: Enable GitHub's dependency scanning and Dependabot. Add regular security audits via 'swift package describe' and 'swift package audit' (when available). Pin dependency versions and regularly review updates.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.