My Read It Later App
2025-01-25
A year ago, to this day, I started building a read it later app for my own. I’ve been actually using it almost daily in that period. This post will provide my motivations and some technical background.
Motivation
The other apps I’ve tried required me to create an account. I don’t want any cross device sync, so I found that annoying. Related to that, they try to upsell me at every chance they get. Highlight some text passage to copy it? UPGRADE TO PRO FOR CREATING ANNOTATIONS!
My favorite iOS app, NetNewsWire, sucks at the „read later“ thing. That’s totally okay, I appreciate software doing one thing and doing it well. In this case, that’s being an RSS client not a reading app. The main thing my app adds here, is persisting my reading progress and scroll position for each article.
I wanted to check out Swift as a language, specifically the SwiftUI framework. Generally, I haven’t done any iOS development work, so it was a good opportunity to try.
Technical Background
I followed the guidelines that the official Swift documentation offered. Straying from the trodden path in an unknown ecosystem is risky and leads to a lot of frustration. I ended up with a well known model-view-viewmodel architecture:
- Models: The app stores all its data in a SQLite database. There’s a lean model layer translating from table rows to Swift structs. The model is querying the database using handcrafted queries, no ORM.
- View Models: These interface with the different models, aggregating and formatting data as required by the views. They’re simple Swift classes that are tagged observables.
- Views: These are SwiftUI components for rendering the actual UI and managing the UI state. Updating the UI when the data changes happens mostly automatically with SwiftUI. It has similar constructs as React has (state, props, context).
The flow of adding a link for later reading is straightforward. The app bundles an iOS sharing extension. This allows me to share URLs using the native share feature. The extension stores the URL in the database, then shows the user a small hint that everything went as expected. When the main app is opened, it looks for new links. If any, it will download the HTML, parse it and extract the title and text content. Those will be stored on the device.
Text Extraction
Extracting the main content of a downloaded HTML source is quite challenging to get right in most cases. I’ve opted to implement the core of the Postlight “reader” library in Swift. It uses a collection of heuristics to score HTML leaf nodes. The score reflects how likely a specific node contains meaning full text. The scores of parent nodes are added up from their child nodes going up the tree. We pick the node with the highest score as the root node for the text extraction. Doing a bunch of clean-up before and after scoring gives quite robust results. And it was a good exercise for learning Swift because I could focus on writing the code. The business logic was already there.
I haven’t converted the full library and not any of the domain-specific extractors. But for my reading habits, it mostly does the job. Images and embeds are tricky, as are sites relying on JavaScript to display their content.
Conclusion
Coming from JavaScript and Scala, Swift wasn’t difficult to pick up. It’s a nice, robust language that doesn’t make things too complicated. Xcode is a beast if you’ve never done iOS development before. I was able to resolve most issues by searching the web. I don’t like it as an editor, it feels clunky. It feels good to build something that I actually end up using.
Screenshots

