Content Filters

ContentFilterManager lets you mute stories containing specific keywords or phrases. Filtered stories are hidden from all views, reducing noise without unsubscribing from entire feeds.

Filter Properties

PropertyDescription
keywordThe text to match against story titles/content
isActiveToggle on/off without deleting
matchCountNumber of stories this filter has blocked
scopeWhere to match: title, body, or both
caseSensitiveWhether matching is case-sensitive (default: no)

Limits

Usage

let mgr = ContentFilterManager.shared

// Create a filter to mute clickbait
let filter = ContentFilter(keyword: "you won't believe")
mgr.addFilter(filter)

// Check if a story should be shown
if mgr.shouldDisplay(story) {
    // show the story
} else {
    // story matches an active filter — hidden
}

// Import/export filters as JSON
let json = mgr.exportAsJSON()
let result = mgr.importFromJSON(jsonString)
print("Added: \(result.added), Skipped: \(result.skipped)")
Tip: Use content filters for recurring noise (e.g., "sponsored", "BREAKING") and Smart Feeds for content you want to find. They complement each other.

Article Highlights

ArticleHighlightsManager lets you save text snippets from articles with color coding. Think of it as a highlighter pen for your RSS reader.

Capabilities

🎨 Color-Coded

Assign colors to highlights for visual organization. Group by theme (yellow for quotes, green for facts, red for follow-up).

🔍 Searchable

Full-text search across all highlights. Find that snippet you saved weeks ago without remembering which article it came from.

📤 Exportable

Export highlights as JSON for backup or sharing. Import highlights from other devices or users.

Usage

let mgr = ArticleHighlightsManager.shared

// Add a highlight
let highlight = ArticleHighlight(
    articleId: story.guid,
    text: "The key insight is...",
    color: .yellow,
    note: "Follow up on this"
)
mgr.addHighlight(highlight)

// Find highlights for an article
let articleHighlights = mgr.highlights(for: story.guid)

// Search all highlights
let results = mgr.search("key insight")

Limits

Maximum 1000 highlights across all articles. Stored in UserDefaults via NSSecureCoding. Each highlight stores the article ID, selected text, color, optional note, and creation date.

Article Notes

ArticleNotesManager provides free-form note-taking attached to articles. Unlike highlights (which capture existing text), notes are your own thoughts and annotations.

Usage

let mgr = ArticleNotesManager.shared

// Add a note to an article
let note = ArticleNote(
    articleId: story.guid,
    content: "This contradicts the findings in yesterday's paper..."
)
mgr.addNote(note)

// Get all notes for an article
let notes = mgr.notes(for: story.guid)

// Get all notes (for a "My Notes" view)
let allNotes = mgr.allNotes

Article Tags

ArticleTagManager adds a tagging system for organizing articles beyond feed categories. Tags are user-defined labels that can cross-cut feeds.

Examples

let mgr = ArticleTagManager.shared

// Tag an article
mgr.addTag("research", to: story.guid)
mgr.addTag("AI", to: story.guid)

// Get articles with a specific tag
let researchArticles = mgr.articles(withTag: "research")

// Get all tags for an article
let tags = mgr.tags(for: story.guid)

// Get all unique tags
let allTags = mgr.allTags

Reading Goals

ReadingGoalsManager lets users set daily and weekly reading targets and tracks progress automatically.

Goal Types

GoalDescriptionDefault
Daily TargetNumber of articles to read per day0 (disabled)
Weekly TargetNumber of articles to read per week0 (disabled)

Progress Tracking

let mgr = ReadingGoalsManager.shared

// Set goals
mgr.updateGoals(ReadingGoals(dailyTarget: 10, weeklyTarget: 50))

// Check progress
let progress = mgr.currentProgress()
print("Daily: \(progress.dailyRead)/\(progress.dailyTarget)")
print("Weekly: \(progress.weeklyRead)/\(progress.weeklyTarget)")
print("Daily %: \(progress.dailyPercentage)")

Notifications

The manager posts notifications you can observe:

Feed Categories

FeedCategoryManager organizes feeds into user-defined categories (e.g., "Tech", "News", "Hobbies"). Categories provide a folder-like hierarchy for the feed list.

let mgr = FeedCategoryManager.shared

// Create a category
mgr.addCategory("Technology")

// Assign a feed to a category
mgr.assignFeed(feed, toCategory: "Technology")

// Get feeds in a category
let techFeeds = mgr.feeds(inCategory: "Technology")

OPML Import/Export

OPMLManager supports the industry-standard OPML 2.0 format for migrating feeds between RSS reader apps.

Import

let mgr = OPMLManager.shared

// Import from file
let result = mgr.importFromFile(at: fileURL)
print(result.summary)
// "12 feeds imported, 3 duplicates skipped, 1 invalid outlines skipped"

// Import from string
let result2 = mgr.importFromString(opmlString)

Export

// Export all feeds as OPML
let opmlString = mgr.exportToString(title: "My RSS Feeds")

// Export to file
mgr.exportToFile(at: fileURL, title: "My RSS Feeds")

OPML Format

The generated OPML includes:

Compatibility: OPML files from FeedReader work with Feedly, Inoreader, NetNewsWire, Reeder, and most other RSS readers. The format is the universal standard for feed subscription portability.