class
RSSParser
Parses RSS XML feeds into RSSStory objects. Supports loading multiple feeds concurrently with O(1) deduplication by link URL. Thread-safe โ all shared state mutations are serialized on an internal serial queue.
Properties
| Property | Type | Description |
delegate | RSSParserDelegate? | Delegate notified when feed loading completes or fails |
stories | [RSSStory] | Read-only. Accumulated stories from all parsed feeds |
Methods
| Method | Description |
loadFeeds(_ urls: [String]) |
Parse stories from multiple feed URLs concurrently. Calls delegate on the main thread when all feeds complete. Cancels any in-flight load from a previous call. Stories are deduplicated by link URL. |
parseData(_ data: Data) โ [RSSStory] |
Parse stories from in-memory XML data synchronously. Useful for testing and offline scenarios. Returns an array of parsed stories directly. |
Usage
import FeedReaderCore
class MyFeedController: RSSParserDelegate {
let parser = RSSParser()
func fetchFeeds() {
parser.delegate = self
parser.loadFeeds([
"https://feeds.bbci.co.uk/news/world/rss.xml",
"https://techcrunch.com/feed/"
])
}
func parserDidFinishLoading(stories: [RSSStory]) {
print("Loaded \(stories.count) stories")
}
func parserDidFailWithError(_ error: Error?) {
print("Feed load failed: \(error?.localizedDescription ?? "unknown")")
}
}
protocol
RSSParserDelegate
Delegate protocol for receiving RSS parsing results. Callbacks are always delivered on the main thread.
Required Methods
| Method | Description |
parserDidFinishLoading(stories: [RSSStory]) |
Called when all requested feeds have finished loading. The stories array contains deduplicated results from all feeds. |
parserDidFailWithError(_ error: Error?) |
Called when a feed fails to load. The error may be nil for non-HTTP errors. |
class
RSSStory
Represents a single parsed RSS story with title, body, link, and optional image URL. Provides URL validation and HTML sanitization. Conforms to NSObject and Sendable.
Properties
| Property | Type | Description |
title | String | The story headline |
body | String | The story description with HTML tags stripped and entities decoded |
link | String | The story's unique URL (used for equality and deduplication) |
imagePath | String? | Optional thumbnail image URL (only set if URL passes safety validation) |
Initializer
public init?(
title: String,
body: String,
link: String,
imagePath: String? = nil
)
Returns nil if:
title is empty
body is empty after HTML stripping
link is not a valid HTTP/HTTPS URL
Static Methods
| Method | Description |
isSafeURL(_ urlString: String?) โ Bool |
Validates that a URL uses only allowed schemes (http, https). Rejects javascript:, file:, data:, and other unsafe schemes. |
stripHTML(_ html: String) โ String |
Strips HTML tags via regex and decodes common HTML entities (&, <, >, ", ', ). |
Equality
Two RSSStory instances are equal if their link properties match. This is how deduplication works across multiple feeds.
class
FeedItem
Represents an RSS feed source with a name, URL, and enabled state. Conforms to NSSecureCoding for persistent storage and Sendable for thread safety.
Properties
| Property | Type | Description |
name | String | Display name for the feed |
url | String | RSS feed URL string |
isEnabled | Bool | Whether the feed is currently enabled for fetching |
identifier | String | Computed. Lowercased URL used for deduplication |
Initializer
public init(
name: String,
url: String,
isEnabled: Bool = false
)
Static Properties
| Property | Description |
presets: [FeedItem] |
10 built-in feed sources: BBC World News, BBC Technology, BBC Science, BBC Business, NPR News, Reuters World, TechCrunch, Ars Technica, Hacker News, The Verge |
Usage
let feeds = FeedItem.presets
let enabledUrls = feeds
.filter { $0.isEnabled }
.map { $0.url }
let custom = FeedItem(
name: "My Blog",
url: "https://myblog.com/rss.xml",
isEnabled: true
)
enum
NetworkReachability
Provides a simple check for network connectivity using SystemConfiguration. Uses SCNetworkReachability to check for a default network route.
Static Methods
| Method | Description |
isConnected() โ Bool |
Returns true if the device currently has a network route available. Does not guarantee that a specific host is reachable โ only that the system believes a route exists. |
Usage
import FeedReaderCore
if NetworkReachability.isConnected() {
parser.loadFeeds(feedUrls)
} else {
showCachedStories()
}