Skip to content

ctaloi/spool

Repository files navigation

Spool

A calm iPhone & iPad reader for Hacker News. On-device AI summaries, a hands-free audio queue, zero tracking. The whole thing runs on your phone.

Site: getspool.news Platform: iPhone & iPad, iOS 26+ Stack: SwiftUI, SwiftData, Apple Foundation Models, AVSpeechSynthesizer


What it does

  • Every HN feed — Top / New / Best / Ask / Show / Jobs, plus Trending (computed locally from score snapshots) and Best-of windows for today / week / month / year.
  • On-device AI summaries. Tap Summarize on any story. Apple's Foundation Models LLM produces a structured take on the article and a sentiment-aware digest of the discussion. No data leaves your phone, no API keys, no cloud fallback.
  • The Spool — your audio queue. Add stories to your Spool, then hit Play All. The on-device model reads each one's article + comments as conversational prose. Sentence-by-sentence highlighting follows the spoken text. Pre-rendered to a cached .caf so playback is instant; runs through MPNowPlayingInfoCenter so AirPods, CarPlay, and the lock screen all work.
  • Editable AI prompts. Every system prompt — article summary, comments summary, audio variants, Q&A — is editable in Settings. Stored on-device, versioned against the bundled default.
  • Threaded comments with collapsible subtrees, ask-questions-of- the-thread, and a sentiment-aware comments summary.
  • Configurable widget. Pin Top / New / Best / Ask HN / Show HN / Jobs to your home screen. Long-press to switch the feed.
  • Sign in to vote / submit / reply. Cookie auth scraped from the HN web flow; tokens live in URLSession.shared.httpCookieStorage, never on disk, never in the codebase.
  • Mentions background-refresh notifications for replies to your comments. Off by default; opt-in toggle in Settings.
  • Saved stories, read tracking, Spotlight indexing, share extension, Siri shortcuts.
  • iPad — three-column Liquid Glass. Native NavigationSplitView, no custom drawers.
  • No telemetry. NSPrivacyTracking: false in PrivacyInfo.xcprivacy. No analytics SDKs. No third-party scripts.

Build

Spool.xcodeproj is generated by XcodeGen.

brew install xcodegen          # one-time
git clone https://github.com/<you>/spool.git
cd spool
xcodegen generate
open Spool.xcodeproj

Code signing (per-clone setup)

Your team ID lives in Configs/Signing.xcconfig, not in project.yml. After cloning:

# Edit Configs/Signing.xcconfig — set DEVELOPMENT_TEAM to your Apple Dev team ID
git update-index --skip-worktree Configs/Signing.xcconfig

The skip-worktree flag keeps your local team ID from getting picked up as a pending change. To pull updates to the file later, reverse it with --no-skip-worktree.

Command-line build

DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer \
  xcodebuild -project Spool.xcodeproj -scheme Spool \
  -destination 'generic/platform=iOS Simulator' build

If your xcode-select points at CommandLineTools (no xcodebuild on PATH), the DEVELOPER_DIR prefix above force-points at Xcode.app.

Architecture

The app is pure SwiftUI + Foundation. No third-party dependencies.

Layer Stack
UI SwiftUI (NavigationSplitView root, iPhone collapse to NavigationStack)
Persistence SwiftData (@Model for SavedStory, ReadStory, SpooledStory, FollowedUser, SeenMention, ScoreSnapshot)
LLM Apple Foundation Models — LanguageModelSession. Errors classified by SummaryError.classify(_:)
TTS AVSpeechSynthesizer.write(_:toBufferCallback:) for offline render; AVAudioPlayer for cached playback
HN data Firebase API (Realtime DB) via HNAPI, Algolia for search / best-of
Auth Cookie-based, scraping auth / hmac tokens from HN's web flow

Project layout

Spool/
  SpoolApp.swift              @main; ModelContainer setup, splash, deep-links
  Theme.swift                 design tokens (Opacity, AnimationDuration, Spacing, CornerRadius, Layout) + Typography
  Models/                     domain types
    HNItem.swift              API item + HNStoryFeed
    HNUser.swift              profile shape
    MainFeedSource.swift      sidebar source enum (category / trending / bestOf / saved / spool / archive / following / mentions)
    AppRouter.swift           cross-view deep-link router (spool://story/<id>, spool://feed/<keyword>)
    Persistence.swift         @Model entities
    SettingsKeys.swift        centralized @AppStorage keys
    Sentence.swift            script splitter for the now-playing scroller
  Networking/                 actors wrapping HN / Algolia / TTS / LLM
    HNAPI.swift               Firebase reader (NSCache-backed)
    HNSearchService.swift     Algolia search
    HNAuthService.swift       cookie auth + vote/submit/reply
    HNUserService.swift       user profile + activity
    HNMentionsService.swift   replies to your comments
    ArticleFetcher.swift      HTML strip → plain text for summarizer
    ImageFetcher.swift        og:image lookup + thumbnails
    DominantColor.swift       per-row accent extraction
    SummaryService.swift      Foundation Models wrapper
    SummaryPrompts.swift      editable prompt resolution
    SummaryPrefetcher.swift   background text + audio prefetch for the Spool queue
    AudioRenderer.swift       offline TTS render → .caf
    AudioCache.swift          per-key on-disk cache with global render queue
    SpoolPlayer.swift         playback (AVAudioPlayer for cached, AVSpeech fallback) + MPNowPlayingInfo + RemoteCommands
    PrefetchGate.swift        foreground-vs-background coordination for the LLM session
    MarkdownStripper.swift    markdown → plain text for TTS
    MentionsNotifier.swift    background-refresh check
    TrendingService.swift     local score snapshots → velocity
    SavedStoryIndexer.swift   Spotlight integration
  ViewModels/                 @MainActor ObservableObjects
    StoryListViewModel.swift / StoryDetailViewModel.swift
    SummaryViewModel.swift / CommentsSummaryViewModel.swift / ThreadQuestionViewModel.swift
    AlgoliaFeedViewModel.swift / TrendingFeedViewModel.swift / FollowingFeedViewModel.swift / MentionsFeedViewModel.swift
    AuthViewModel.swift / UserProfileViewModel.swift
  Views/                      SwiftUI views
  AppIntents/                 Siri shortcuts
  Assets.xcassets             multi-appearance AppIcon (light / dark / tinted)
  PrivacyInfo.xcprivacy
SpoolWidget/                  home-screen widget (configurable via AppIntent)
SpoolShareExtension/          system share-sheet target
tools/
  make_icon.swift             Core Graphics AppIcon generator
landing/showcase/             marketing site (deployed at getspool.news)

Privacy

  • NSPrivacyTracking: false in PrivacyInfo.xcprivacy.
  • No analytics SDK. No third-party scripts. No proxy server.
  • HN login is cookie-based directly to news.ycombinator.com. Cookies live in URLSession.shared.httpCookieStorage (system keychain), never on disk, never in the codebase.
  • The on-device LLM is gated on Apple Intelligence being enabled in iOS Settings. If it's off, summary features show a Settings deep- link instead of failing silently.

License

MIT — see LICENSE.

Contributing

Issues and PRs welcome at github.com/ctaloi/spool.

About

SwiftUI Hacker News reader for iOS 26 with on-device AI summaries and Liquid Glass UI

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors