Open-source iOS radio app with a SwiftUI interface. Used by 80+ apps on the App Store.
Looking for Android? See Swift Radio Android. Same station format, native Kotlin and Jetpack Compose.
- Stream live radio with background audio playback
- Scrubber and progress bar for non-live streams
- Apple CarPlay support
- Album art and track metadata from streams and iTunes API
- Lock screen and Control Center integration
- Multiple stations from a local or remote JSON file
- Pull to refresh and optional search bar
- Localization-ready via Xcode String Catalog
- About screen with email, sharing, and external links
Built on FRadioPlayer for streaming, metadata parsing, and iTunes album art fetching.
- iOS 17 or later: the deployment target of the app target, the CarPlay target, and the
SwiftRadioCorepackage. - Xcode 26.6 or later: CI builds the app and CarPlay targets with Xcode 26.6 and 27.0. The test suites are run with Xcode 27.
- Swift 6 language mode for the app targets and
SwiftRadioCore, with complete concurrency checking.
Dependencies resolve through Swift Package Manager, so there's nothing to install before the first build.
- Open
SwiftRadio.xcodeprojin Xcode - Edit
SwiftRadio/Config/Config.swiftto set your stations URL, contact email, website, and other app-wide settings - Update your stations in
SwiftRadio/Data/stations.jsonor host the file remotely - Build and run
- Add an entry to
SwiftRadio/Data/stations.json, or to the JSON you host atConfig.stationsURL - Add a square image to
SwiftRadio/Images.xcassets/Stations/and name the image set exactly like the station'simageURL - Build and run: no code change is needed
Each station supports these fields:
{
"name": "Station Name",
"streamURL": "https://stream.example.com/live",
"imageURL": "station-image",
"desc": "Short tagline",
"longDesc": "Longer description for the detail screen",
"website": "https://example.com"
}imageURL: an asset name for a bundled image, a fullhttp(s)URL for a remote one, or an empty string to fall back to thestationImageplaceholder.website: optional. The "Visit Website" row and the popup's website option appear only when it starts withhttporhttps.longDesc: optional. Omit the key or leave it empty and the station info screen shows a default description.
The template works at any catalog size, so a single-station app is mostly configuration:
- Trim the catalog: leave one entry in
stations.jsonand keepConfig.useLocalStations = true. - Drop the controls it doesn't need: set
Config.hideNextPreviousButtons = trueand leaveConfig.searchable = false. - Open on the player: call
StationsStore.select(_:)once the catalog loads and setRootView'spopupOpentotrue. The one-row list stays behind the player as the back destination. - Rebrand: replace
logoand the station image inImages.xcassets, then edit the strings inLocalizable.xcstrings.
All user-facing strings are managed through Localizable.xcstrings (the String Catalog). Open it in Xcode to change the default English text or add new languages. Most views read their strings through Content.swift; a few accessibility labels in the player and toolbar use their catalog keys directly (player.next, player.seek, common.close and similar). Either way the text lives in the catalog, so a translation needs no code change.
SwiftUI owns the app's screens and navigation. AppEnvironment creates shared services for the phone and CarPlay, so both interfaces use the same station selection and playback state.
SwiftRadioCore is a local Swift package that separates playback, station catalogs, artwork and system-media integration from presentation. Its dependency boundaries let us test playback and catalog behavior without launching the UI. Small UIKit adapters remain for the popup player, marquee text, indicators and system presentations.
See the SwiftRadioCore guide for service boundaries and the architecture guide for the source map, popup flow and system controls.
Run the core tests from Packages/SwiftRadioCore using the package guide. They cover playback, catalog refresh, remote commands, Now Playing and interruptions without live streams.
The visual parity protocol covers screenshots, accessibility and UI regression commands. CI also builds both app targets and validates an unsigned archive. Physical-device audio routes, lock-screen controls and CarPlay remain separate acceptance checks.
| Library | Purpose |
|---|---|
| FRadioPlayer | Audio playback, stream metadata and artwork lookup, behind SwiftRadioCore |
| LNPopupUI | SwiftUI popup bar and expanded player |
| MarqueeLabel | Scrolling title in the expanded player |
| NVActivityIndicatorView | Playback equalizer and buffering animations |
Swift Package Manager manages these libraries. LNPopupUI also brings in LNPopupController, LNSwiftUIUtils and, through LNPopupController, LNSystemMarqueeLabel. SwiftRadioCore is included in this repository as a local package. The checked-in lockfiles record the exact resolved versions.
Contributions are welcome. Please branch off dev and open a pull request. Do not commit directly to master.
Looking for a simpler, single-station version? It skips the station list and launches straight into the player. One purchase now includes both the iOS and Android single-station projects.
All proceeds go directly toward maintaining and improving Swift Radio.
For custom work or more advanced needs, reach out to Fethi.
Built something with Swift Radio? We'd love to see it. Drop us a line at contact@fethica.com.
- Fethi, co-organizer and lead developer
- Matthew Fecher, creator, AudioKit Pro
- All contributors
Swift Radio is open source and available under the MIT License.
