SyndicationFeed is a comprehensive Swift package designed to parse RSS feeds with a focus on podcast content, including full support for the Podcast Index RSS 2.0 specification. Whether you're building a podcast app, analyzing RSS feeds, or working with modern podcasting features, this package provides a robust, type-safe, and async-first approach to feed parsing.
Built with clarity and extensibility in mind, SyndicationFeed handles standard RSS elements alongside advanced podcast-specific tags like <podcast:transcript>, <podcast:chapters>, <podcast:value>, and other innovative features of the evolving RSS 2.0 spec.
- ๐ง Simple async API for parsing RSS feeds from URLs, strings, or data
- ๐ก Codable-based models for seamless Swift integration
- ๐ก Multi-namespace support including RSS 2.0, iTunes/Apple Podcasts, and Podcasting 2.0
- ๐ง Comprehensive tag support for modern podcast features
- ๐งช Built-in validation and detailed error handling
- โก Performance optimized with streaming XML parsing
- ๐ก๏ธ Type-safe Swift models for all feed elements
- ๐ฑ Platform support for macOS 13.0+
Add SyndicationFeed to your Swift package dependencies in Package.swift:
dependencies: [
.package(url: "https://github.com/fitomad/SyndicationFeed.git", from: "0.9.0")
]Or add it through Xcode's Package Manager by entering the repository URL.
import SyndicationFeed
let syndicationFeed = SyndicationFeed()
// Parse from URL
do {
let url = URL(string: "https://example.com/podcast.xml")!
let result = try await syndicationFeed.fetchFeedFrom(url: url)
let channel = result.channel
print("Podcast: \(channel.title)")
print("Episodes: \(channel.items.count)")
// Access podcast-specific data
if let podcastInfo = channel.podcasting {
print("GUID: \(podcastInfo.guid?.uuidString ?? "N/A")")
print("Medium: \(podcastInfo.medium?.rawValue ?? "N/A")")
}
} catch let error as SyndicationFeedError {
print("Parsing error: \(error)")
}let xmlContent = """
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:podcast="https://podcastindex.org/namespace/1.0">
<channel>
<title>My Podcast</title>
<description>A great podcast</description>
<!-- ... more content ... -->
</channel>
</rss>
"""
do {
let result = try await syndicationFeed.fetchFeedFrom(content: xmlContent)
let channel = result.channel
// Process the parsed feed...
} catch {
print("Error: \(error)")
}let feedData = // ... your RSS feed data
let result = try await syndicationFeed.fetchFeedFrom(data: feedData)- Channel: title, description, link, language, copyright, etc.
- Items: title, description, publication date, enclosures, etc.
- Media: enclosures with full metadata support
- Categories: hierarchical category support
- Channel metadata: author, categories, explicit content flags
- Episode data: duration, episode numbers, season numbers
- Content classification: episode types, explicit content markers
- Images: high-resolution artwork support
- Identity: GUID, locked status
- Rich media: alternate enclosures, chapters, transcripts
- Monetization: value blocks, funding information
- Social features: person tags, social interaction
- Discovery: trailers, seasons, episodes
- Live content: live item support
- Location data: geographic information
- Content links: web-based content references
The package is built with a clean, modular architecture:
SyndicationFeed/
โโโ Entities/ # Data models
โ โโโ RSS/ # Standard RSS elements
โ โโโ Apple/ # iTunes/Apple Podcasts elements
โ โโโ Podcast/ # Podcasting 2.0 elements
โโโ Parsers/ # XML parsing logic
โโโ Handlers/ # Tag-specific parsing handlers
โโโ Mappers/ # Data transformation utilities
โโโ Extensions/ # Utility extensions
The main result type containing the parsed channel and any parsing errors:
public struct FeedResult {
public let channel: Channel
public let parsingErrors: [SyndicationFeedError]?
}Represents the main podcast/feed information:
public struct Channel {
public var title: String
public var description: String?
public var items: [Item] // Episodes
public var podcasting: Podcasting? // Podcasting 2.0 data
public var iTunes: Apple? // iTunes/Apple data
// ... other RSS properties
}Represents individual episodes or items:
public struct Item {
public var title: String?
public var description: String?
public var enclosure: Enclosure?
public var podcasting: Podcasting? // Episode-specific podcast data
public var iTunes: Apple? // iTunes episode data
// ... other properties
}SyndicationFeed provides detailed error information through the SyndicationFeedError enum:
do {
let result = try await syndicationFeed.fetchFeedFrom(url: feedURL)
// Check for parsing warnings
if let errors = result.parsingErrors {
for error in errors {
switch error {
case .unavailableTag(let tagName, let element):
print("Unknown tag '\(tagName)' in '\(element)'")
case .malformedTagValue(let value, let tag):
print("Invalid value '\(value)' for tag '\(tag)'")
// Handle other error types...
default:
print("Parsing warning: \(error)")
}
}
}
} catch SyndicationFeedError.contentNotFound {
print("Feed content not found or inaccessible")
} catch SyndicationFeedError.malformedContent {
print("Feed content is malformed or invalid XML")
} catch {
print("Unexpected error: \(error)")
}let result = try await syndicationFeed.fetchFeedFrom(url: podcastURL)
let channel = result.channel
// Check if this is a locked podcast
if let locked = channel.podcasting?.locked {
print("Podcast is locked by: \(locked.owner)")
}
// Access value/payment information
if let values = channel.podcasting?.values {
for value in values {
print("Payment method: \(value.method)")
print("Recipient: \(value.address)")
print("Split: \(value.split)%")
}
}
// Process episodes with transcripts
for item in channel.items {
if let transcripts = item.podcasting?.transcripts {
for transcript in transcripts {
print("Transcript available at: \(transcript.url)")
print("Language: \(transcript.language ?? "unknown")")
}
}
}The package includes comprehensive tests with sample RSS feeds:
swift testTest resources include:
- iTunes-compatible feeds
- Podcasting 2.0 example feeds
- Various RSS versions (0.91, 0.92, 2.0)
- Edge case scenarios
- Swift: 6.0+
- Platform: macOS 13.0+
- Dependencies: Foundation only (no external dependencies)
This project is licensed under the MIT License. See the LICENSE file for details.
- Podcast Index for the Podcasting 2.0 namespace specification
- Apple for the iTunes podcast specification
- The RSS specification maintainers
- Support for...
- RSS
- iTunes
- Podcasting 2.0
- BlueSky @fitomad.bsky.social
- Mastodon @fitomad
- LinkedIn Adolfo Vera
Let your Swift code speak fluent podcast. ๐๏ธ