Matrix for NetNewsWire

0:00
/0:02

Matrix

This theme is *Matrix-*inspired and was built with the help of Claude (there’s simply no way my CSS is this clean and tidy). This theme shines when your device is in dark mode.

Features

Visual Style

  • Phosphor-green palette — deep near-black background with glowing green text
  • CRT vignette — a radial gradient overlay darkens the screen edges for a vintage monitor feel (macOS only)
  • Boot flicker — the page and article animate in with a CRT power-on flicker effect
  • Monospace throughout — SF Mono

Article Header

  • Feed name is prefixed with a shell prompt: user@nnw:~$ cat
  • Publish date is prefixed with # timestamp:
  • External link is prefixed with # source:
  • Feed icon is rendered in greyscale with a green tint and a pixel-art rendering mode

Article Title

  • Typewriter animation — the title types out character by character in reading order, correctly handling titles that span multiple lines
  • Blinking block cursor () appears after the last character lands and blinks indefinitely

Article Body

  • Separator line of characters between the header and body
  • Headings prefixed with Markdown-style ##, ###, #### markers
  • Blockquotes styled with a left border and faint green background
  • Code blocks include a fake terminal title bar (● ● ● output) and horizontal scroll on overflow
  • Inline code highlighted in amber with a subtle glow
  • Tables use uppercase headers, row hover highlights, and a green glow on the border
  • Figcaptions prefixed with //
  • Images are desaturated and dimmed; hovering partially restores colour

Platform Behaviour

  • iOS — uses dynamic type sizing, system hyphenation, and respects safe area insets; html background is set so native navigation and tab bar blur effects sample the correct dark colour
  • macOS — includes the CRT vignette, wider padding, and fixed text-size classes (smallTextxxlargeText)

Add to NetNewsWire

Featured

Experiments with the Codex and Claude Agents

Experiments with the Codex and Claude Agents

I’ve been experimenting with agents: Codex on a Python-to-ExpressJS conversion, and Claude in a Swift Package. The results have been impressive.

The remainder of this post was originally for members only.

Codex

Singapore Buses has a back-end server, written in Python with FastAPI, that is used to manage storage of APNS tokens, retrieve bus arrival estimates and push that data out as Live Activity notifications, and storage of aggregated session events for users. I wrote the server application in Python simply to get some experience with the language. I have, however, been wanting to re-write it as an ExpressJS app for quite some time.

Enter Codex.

I downloaded Codex and gave it permission to the project’s directory. I gave it one prompt:

Can you convert this project to ExpressJS?

Codex took less than two minutes to convert public routes, middleware, database, and schedulers, while selecting and replacing third-party Python packages with similar npm packages (e.g., apns2 was replaced with @parse/node-apn).

The results was a working application with one defect—the payload for Live Activities was empty 🫣. Once that was fixed and the rest of the code tested, it was more-or-less a drop in replacement on the server (after changing PM2’s configuration file).

Claude

In Xcode, I enabled Claude’s agent and gave it dominion over my Swift Package LandTransportKit which is what I use in Singapore Buses. The goal of this experiment was to have it write documentation for the package as a Documentation Archive (DocC). This is something I’ve been meaning to do for quite some time, as I wrote when I published the package in July 2025:

I used the ChatGPT integration to write the documentation for most of struct , class, and func definitions. It hasn’t been used for the code itself, the test cases, or DocC.

The prompt:

Write documentation for this package in the documentation archive.

The results speak for themselves. Not only is the package extensively documented, there’s also example usage in UIKit and SwiftUI, along with prebuilt SwiftUI Views.


These experiments have left me impressed with what the Codex and Claude agents are capable of. Both were fast, understood the prompts, and, ultimately, I got the results I wanted.

NetNewsWire 7 for iOS Out Now

NetNewsWire 7 for iOS Out Now

After five-ish years of NetNewsWire 6—and many Summer, Autumn, and Winter nights of coding through 2025 and into 2026, and a sizeable TestFlight window—The World’s Favourite Open Source RSS ReaderTM has reached version 7. (Note: we discovered and fixed a lot of bugs during TestFlight, so thanks go to the testers!)

Similar to the Mac release, NetNewsWire 7 for iPhone and iPad:

  • requires the OSs 26
  • adopts Liquid Glass
  • is a significant under-the-hood overhaul that adopts Swift Concurrency

But, but, but…unlike the Mac release, which was quite easy, the iOS release has required a lot more work. On the latest episode of *The Talk Show, *Brent rightly pointed out that “iOS apps are just more complicated”.

To summarise my Design Diaries and some additional items, NetNewsWire 7 makes 30 major changes:

  • [Sidebar] Converted from UITableView to UICollectionView. This was needed in order to adopt modern styling across iPad and iPhone. iPad uses the .sidebar style, and iPhone uses .insetGrouped. This is similar to the behaviour you see in Mail.
  • [Sidebar] The current *Refresh *status is now located in the navigation bar as a subtitle, having previously been the footer.
  • [Sidebar (iPad)] Like the Mac refresh, the Feeds view floats and allows Timeline content to slide underneath.
  • [Sidebar] Smart Feeds and Account headers now adopt modern secondary styling.
  • [Sidebar (iPad)] Selected feeds have a modern capsule background and the text is bold.
  • [Sidebar] Folders have been redesigned to match modern standards—they now have the same indentation as any other feed, but the enclosed feeds are indented further.
  • [Sidebar] Folders will highlight when Feeds are being dragged and dropped into them.
  • [Sidebar] Separators have been realigned.
  • [Sidebar] Unread counts are larger and are no longer backed by a filled capsule.
  • [Sidebar] Unread counts for folders are only displayed when the folder is closed.
  • [Sidebar] Swipe actions reveal icons.
  • [Sidebar (iPad)] Users can resize the sidebar (within reason).
  • [Timeline] Converted from UITableView to UICollectionView (during TestFlight builds!) This was needed in order to adopt modern cell styling—e.g., selected and swipe status—across iPad and iPhone.
  • [Timeline] Now uses UICollectionViewDiffableDataSource.
  • [Timeline] Navigation bar images have been removed.
  • [Timeline] Unread counts are now located in the navigation bar subtitle.
  • [Timeline] Adopts hierarchical text colours for titles and summaries.
  • [Timeline (iPad)] The search bar has been moved to the app-wide toolbar and behaves similar to search on the Mac.
  • [Timeline (iPhone)] The search bar has been moved to the bottom toolbar.
  • [Timeline (iPad)] The Timeline width is user adjustable (again, within reason).
  • [Timeline] Timeline cells have been redesigned in Interface builder and now have the rounded corner selection style in addition to hierarchical text colours for title and summary.
  • [Timeline] The Mark All as Read image (on both iPad and iPhone) has had alignment changes to make sure it sits in the middle of an englassified button.
  • [Article (iPad)] Articles can be read in three-pane view without hiding the Sidebar.
  • [Article (iPad)] The top toolbar inherits search capabilities.
  • [Article] The bottom toolbar buttons have been grouped in a 2-1-2 formation with the *Next Unread *button sitting in the throne seat.
  • [Sidebar, Timeline, Article] Visual state is restored on relaunch.
  • [Widgets] Home Screen widgets have been redesigned to make better use of horizontal space.
  • [Widgets] Added a new Lock Screen widget with Today, Unread, Starred counts.
  • [Settings] Timeline Customiser has been redesigned and includes both icon and non-icon previews.
  • [About] Tending to the dark corner of the garden, the About view on iOS has been redesigned and inspired by the Credits from Vesper.

The new About view.

NetNewsWire 7 for Mac Out Now

NetNewsWire 7 for Mac Out Now

NetNewsWire 7 for Mac is out now.

This release is significant: it adopts Swift Concurrency (which you shouldn’t see), Liquid Glass (which, despite the transparency, you should see), and supports macOS 26 (as a minimum). From a UI perspective:

  • The Sidebar adopts standard Liquid Glass behaviours which means it floats and allows timeline content to slide underneath
  • Unread indicators are no longer backed by a filled capsule. They are now just a simple unread count
  • The Toolbar has seen a minor reorganisation which moves the sidebar toggle from the timeline into the sidebar. In addition, toolbar buttons adopt the standard Liquid Glass button look-and-feel
  • Tending to the dark corner of the Lucida Grande garden, the About window has been redesigned

Nick Heer, Pixel Envy:

This is a rather tasteful implementation of Apple’s new visual design language

The iOS release is just around the corner.

A Triple-Negative Pretzel

I could not fail to disagree with you less. — Boris Johnson

My interpretation of this ridiculous sentence:

  • “fail to disagree”* *equates to “agree”, therefore, with some substitution:
  • “I could not agree with you less” suggests you’re at the maximum end of the disagreement scale.

Regardless, I asked both ChatGPT, Claude, and Gemini what the *plain English *meaning of this statement was and got two completely opposing answers:

  • ChatGPT described the sentence as a *triple-negative pretzel, *with the plain English meaning: I completely disagree with you.
  • Claude and Gemini both stated the opposite: I totally agree with you.

So what does it mean?

Changes to tabViewBottomAccessory in iOS 26.1 are Fixed in iOS 26.2

Changes to tabViewBottomAccessory in iOS 26.1 are Fixed in iOS 26.2

[Update 09 Nov 2025: there’s new API in iOS 26.2 that allows you to hide the tabViewBottomAccessory. See the bottom of this post for more.]

In Singapore Buses on iOS 26, I was able to hide the tabViewBottomAccessory based on various criteria—for example, if location tracking was enabled.

.tabViewBottomAccessory {
if locationTracker.isAuthorised {
HStack {
// Nearby bus stop
}
} else {
EmptyView()
}
}

Now, however, it displays the accessory with no content. I haven’t found a workaround yet, so for now I’m showing the tabViewBottomAccessory across all views. This isn’t ideal—the accessory isn’t relevant on the Settings view, for instance.

If this is Apple’s intended behaviour for the accessory, it’s far from ideal.

Update 09 Nov 2025:

There’s an update to the tabViewBottomAccessory modifier API in iOS 26.2 that lets you hide the tabViewBottomAccessory when it’s not needed:

.tabViewBottomAccessory(isEnabled: $showShowBottomAccessory) {
HStack {
// Nearby bus stop
}
}

Problematically, this only works with iOS 26.2. As I don’t intend to move away from iOS 26.0 as the minimum requirement for *Singapore Buses *for several months, I will apply this API using an if #unavailable(iOS 26.1) check. Users on iOS 26.0 - 26.1 will see the accessory on all views. 😞

Halo: Campaign Evolved

Halo: Campaign Evolved

I have mixed feelings about Halo: Campaign Evolved. The trailer looks and sounds excellent, the gameplay is both improved and familiar, and the nostalgia vibes are strong.

And that’s the problem: this is the third version of the Halo: Combat Evolved campaign (Halo: Combat Evolved (2000); *Halo: Combat Evolved Anniversary *(2011); and now Halo: Campaign Evolved (2026)).

I really hope *Halo: Campaign Evolved *is good. But it’s trading entirely on nostalgia.

That leaves me with some obvious questions:

  • How does Halo: Campaign Evolved move the franchise forward (excluding the fact that it’ll be on PlayStation)?
  • Can a 26-year-old game hold its own against modern FPS games or is this going to be like the Goldeneye64 remake?
  • Split-screen local co-op, online co-op, and no multiplayer…there’s more to announce, surely?

Joining the Streaming Game

Joining the Streaming Game

For years, both at home and at work, I’ve used headsets with built-in microphones that sit uncomfortably close to my mouth. They’ve always irritated me, especially when drinking coffee and the mug inevitably whacks the mic arm mid-sip.

To address this horrific shortcoming, I added a Shure MV6 microphone to my desk and now I have a new hobby — streaming games at @flywithstuart on YouTube (things are just getting started). Predominantly, the current focus is the recently released Battlefield 6, with some Microsoft Flight Simulator 2024 thrown in for good measure. I’m considering doing a NetNewsWire bug fix stream in the future (when I setup OBS on my Mac).

Please like and subscribe.

Singapore Buses v2026

Singapore Buses v2026

Singapore Buses v2026 is out now. It’s been a challenging development cycle, to say the least, and I’m glad it’s over. The Design Diaries series covers the major Liquid Glass updates. This post looks at some of the other technical changes that have been made in the v2026 update.

GRDB

Singapore Buses, has always, always, always—way back to 2018 when it was SG Transit—used Core Data. Even when SwiftUI came along, followed by SwiftData, it stuck with Core Data. Not any more. It’s now using GRDB.

Each app version is seeded with an updated SQLite database containing the latest bus stops, routes, and services. This database is created using a separate app on the Mac. Long-term, the aim is to move away from having to release new app versions with an up-to-date database and instead move to an over-the-air model that refreshes the database in the background. The move to GRDB should smooth the path.

Map Performance

The database is now loaded into memory instead of being read from disk. As a result, panning the map and determining what bus stops to show (or hide) is now instantaneous instead of requiring a small pause and the end of the pan gesture. It’s a simple call site:

.onMapCameraChange(frequency: .continuous) { context in
model.showStops = context.camera.distance < 9500 // hide stops when above 9,500m
Task {
await model.updateVisibleBusStops(context.rect)
}
}

App Clip

Singapore Buses now contains an App Clip with arrival estimate UI.

When users share estimated arrival times from the main app, text is generated with the current arrival times along with a URL: https://clips.singaporebuses.app?... That URL has the following query parameters:

Parameter Usage Example
c Bus Stop Code 08138
n Bus Stop Name Concorde Hotel S'pore
r Road Name Orchard Rd
s Service No 174
lat Latitude 1.3004785730821
lon Longitude 103.841847006076
e Expiry 1757767987
v HMAC Validation signature

The parameters c, n, and r are used to populate the UI in the App Clip, while lat and lon are used to determine if a Look Around view can be displayed. s in conjunction with c are used to query the LTA API for the latest estimates. e is the time the App Clip expires, and v is a signature that is validated to make sure the URL is genuine.

If the recipient has Singapore Buses installed, tapping the link opens the app and presents arrivals for that bus stop. Otherwise, an App Clip launches with the same bus arrival UI as the main app and updates arrival estimates every 15 seconds for 10 minutes…then invites the user to download the app from the App Store.

The **clips**.singaporebuses.app subdomain is powered by Cloudflare Workers. It serves an AASA, some basic content, and a favicon, but is otherwise is there to enable redirects to the app or the App Clip.

This is the first time I’ve implemented an App Clip. I’m interested to see how (or if) it affects download statistics.

iPad Support

Given that iPadOS is, in my opinion, the star of the *26 *series of OS updates, it only made sense to extend Singapore Buses to the iPad. It now has a native layout with some iPad specific niceties, like keyboard shortcuts and a full-screen route views.

(I’ve never seen anyone actually use an iPad at a bus stop in Singapore. With that in mind, Singapore Buses will be on Apple Watch in the future.)

LandTransportKit

Lastly, all interaction with the Land Transport API is now handled with LandTransportKit.

I’ve open sourced this package—it’s free for anyone to use!

Singapore Buses v2026 Beta

0:00
/0:06

Singapore Buses v2026 beta is available now to website members. v2026 is a complete re-write and updates the app with Liquid Glass design niceties.

If you wish join the TestFlight, sign up and you’ll be able to access to the TestFlight link at the bottom of the article.

The remainder of this post was originally for members only.

What’s New in Singapore Buses v2026

OS Requirements

  • [New] The minimum supported iOS version is iOS 26
  • [New] Singapore Buses is now a native iPadOS app and works on Apple Vision Pro in iPad display mode

Underlying Technologies

  • [New] Database interaction is now managed by GRDB instead of Core Data
  • [New] Interaction with the Land Transport Authority API is now handled via LandTransportKit and it’s a little bit quicker
  • [Updated] Location tracking is now more accurate and updates more frequently

User Interface

  • [New] The app now uses the updated Liquid Glass tab bar navigation structure
  • [New] The nearest bus stop is presented above the tab bar on the map view
  • [New] The map view will display bus stops as you scroll instead of at the end of the scroll gesture
  • [New] All map views—bus stop and bus route—can be viewed in 3D
  • [New] Search has been updated with the search bar now positioned at the bottom of the screen
  • [New] App Icon based on 1990’s Singapore bus stop design
  • [New] TipKit used for initial onboarding
  • [Updated] There have been small changes to the bus stop layout rows
  • [Updated] There are additional theme options for Singapore Buses+ subscribers
  • [Updated] There are new support options in Settings
  • [Updated] Acknowledgements view

Join TestFlight