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

PropertyTypeDescription
delegateRSSParserDelegate?Delegate notified when feed loading completes or fails
stories[RSSStory]Read-only. Accumulated stories from all parsed feeds

Methods

MethodDescription
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]) { // Stories from all feeds, deduplicated 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

MethodDescription
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

PropertyTypeDescription
titleStringThe story headline
bodyStringThe story description with HTML tags stripped and entities decoded
linkStringThe story's unique URL (used for equality and deduplication)
imagePathString?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

MethodDescription
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

PropertyTypeDescription
nameStringDisplay name for the feed
urlStringRSS feed URL string
isEnabledBoolWhether the feed is currently enabled for fetching
identifierStringComputed. Lowercased URL used for deduplication

Initializer

public init( name: String, url: String, isEnabled: Bool = false )

Static Properties

PropertyDescription
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

// Use built-in presets let feeds = FeedItem.presets let enabledUrls = feeds .filter { $0.isEnabled } .map { $0.url } // Create custom feed 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

MethodDescription
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 { // Load from cache or show offline UI showCachedStories() }