I built Moji because I was drowning in saved articles. Every read-it-later app I tried became a graveyard of unread links — no structure, no way to surface the right article at the right time. Moji is a native iOS read-it-later app that saves articles for offline reading and organizes them automatically using smart collections. The name "Moji" comes from 墨迹 (mòjì) in Chinese — it literally means "ink traces," but colloquially it means being slow or dawdling. Felt fitting for an app that lets you save things to read later — no rush.
Smart Collections — the core idea
Instead of manually tagging or filing articles, you define criteria and Moji continuously filters your library for you. Criteria combine with AND logic between types and OR logic within a type, so you can build surprisingly precise filters:
- Domain: arxiv.org, paperswithcode.com + Saved: This Week → Fresh ML papers
- Keywords: "SwiftUI", "Combine" + Unread → Your iOS learning queue
- Reading Time: > 15 min + Unread → Weekend deep dives
- Domain: news.ycombinator.com + Saved: Last 7 days → This week's HN saves
- Language: zh + Reading Time: < 5 min → Quick Chinese reads for your commute
Four system collections come built in — Unread, Quick Reads (<5 min), Deep Dive (>10 min), and This Week — so it's useful out of the box. Pin your favorites to the filter bar for one-tap access.
Other features
- Native SwiftUI reader — Articles render as native SwiftUI views, not a WebView. This means real offline reading, smooth scrolling, and proper typography controls (font size, serif/sans-serif, line spacing).
- On-device AI summaries — One-sentence TL;DRs powered by Apple Intelligence. Runs entirely on-device, no cloud calls. Supports 10+ languages.
- Full-text search — Search across titles and content with context snippets that jump you straight to the match in the article.
- Reading position memory — Remembers exactly where you left off, down to the block and scroll offset.
- Image viewer — Pinch-to-zoom, double-tap, pan, alt-text display.
- PDF export — Save any article as a styled PDF.
- Share extension — Save from Safari in two taps.
- Language-aware reading time — Calculates differently for CJK (260 WPM) vs. English (200 WPM) vs. Arabic/Hebrew (150 WPM).
- iCloud sync — Optional CloudKit sync across devices.
- Privacy-first — All processing happens on-device. No analytics, no tracking.
Technical details for the curious
Built with Swift 6.2, SwiftData, structured concurrency, and Mozilla's Readability.js for content extraction. The HTML parser converts articles into typed ContentBlock values that SwiftUI renders natively. A three-phase background pipeline handles extraction, quality re-extraction, and summary generation.
Pricing
Start with a 2-week free trial — all features unlocked, no restrictions. After that, a one-time Pro purchase ($9.99 in US, price may vary in other countries) is required to save new articles. No subscription. You never lose access to your existing library, reading features, or smart collections — the gate is only on adding new articles.
I'd love feedback — especially on the smart collection criteria. What filters would make this more useful for your workflow?
---
App Store Link: https://apps.apple.com/us/app/moji-reader/id6758530352
Pretty cool! (I can't test it, seems unavailable in Germany).
How are you handling the text extraction with Readability.js? Does that run in a WebView in the background? I've built a similar, albeit less feature-complete, read-it later app in the past [1]. Spent most of my rewriting the core of the Postlight parser [2] in native Swift.
Anyway, cool project, and I'd probably buy this once its available.
[1] https://franz.hamburg/writing/read-later-app.html
[2] https://github.com/postlight/parser
Thanks! It should be available in Germany now — let me know if you still have trouble finding it.
Yes, Readability.js is injected into a hidden WKWebView for each extraction. I also recently added Defuddle as a second engine to handle JS-heavy sites and ones with custom extractors (Twitter/X, Reddit, YouTube, Hacker News, GitHub, etc.). A coordinator routes to the right engine based on the domain. After extraction, the HTML gets parsed into typed content blocks and rendered as native SwiftUI views — much nicer for theming and performance than using a web view. The Share Extension just grabs the URL and hands it off to the main app for full processing.
That's really cool that you rewrote the Postlight parser in native Swift — that's a serious undertaking. I considered going fully native for parsing but the JS engines are surprisingly capable when you let them run in a WebView. Really enjoyed reading your write-up too! The scoring approach you took for content extraction is essentially what Readability.js does under the hood too — scoring DOM nodes by heuristics and bubbling scores up to parents. Impressive that you reimplemented that in native Swift.
This feels very similar to Karakeep, but on-device instead of self-hosting.
The screenshots on the website don’t include images, so I was under the impression they’re not supported until I re-read the post here – it might be beneficial to add some.
Thanks for the feedback! Will add some for sure.