📂 Content Management
Tools for organizing, filtering, annotating, and migrating your reading content—from muting noise to highlighting insights.
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
| Property | Description |
|---|---|
keyword | The text to match against story titles/content |
isActive | Toggle on/off without deleting |
matchCount | Number of stories this filter has blocked |
scope | Where to match: title, body, or both |
caseSensitive | Whether matching is case-sensitive (default: no) |
Limits
- Maximum 50 filters — prevents excessive filtering overhead
- Matching uses
localizedCaseInsensitiveContainsfor locale-aware comparison - Filters are persisted via NSSecureCoding to the Documents directory
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)")
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
- Tag articles from different feeds as "research" for a project
- Mark articles as "share-worthy" for later discussion
- Create a "read-later" tag independent of bookmarks
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
| Goal | Description | Default |
|---|---|---|
| Daily Target | Number of articles to read per day | 0 (disabled) |
| Weekly Target | Number of articles to read per week | 0 (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:
.readingGoalsDidChange— goals or progress updated.readingGoalAchieved— daily or weekly target reached 🎉
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:
- Title — customizable document title
- Date — export timestamp
- Outlines — one per feed with
xmlUrl(feed URL),htmlUrl(site URL),title, andtext - Categories — feeds grouped by category as nested outlines