Parsing HTML Fast

The urge to overengineer something recently befell me, and the target of that urge was the way Tusker handles HTML parsing. Mastodon provides the content of posts (and profile descriptions, and some other things) as HTML, which means that, in order to display them properly, you need to parse the HTML, a notoriously straightforward and easy task. For a long time, the approach I took was to use SwiftSoup to parse the HTML and then walk the resulting node tree to build up a big NSAttributedString that could be displayed.

And while that technique worked perfectly well, there were a number of downsides that I wasn’t entirely satisfied with. First: SwiftSoup alone is a big dependency, at around ten thousand lines of code. Since it’s shipping in my app, I take the view that I’m responsible for making sure it doesn’t break—and that’s a lot of surface area to cover (moreover, since it was ported from a Java library, the code is extremely un-Swift-y). Second: it’s not especially fast. Partly because of its aforementioned nature as a Java port (everything is a class, lots of unnecessary copies), but primarily because it’s just doing more than I need it to (the first rule of optimization is do less work): it was parsing a string into a tree and then I was flattening that tree back into a string.

The last time I needed to do something similar, I used lol-html, Cloudflare’s streaming HTML rewriter. But, while that worked (and is still in use for my RSS reader app), it’s written in Rust, and I didn’t want to introduce significant additional complexity to the build process for Tusker. So, writing a new HTML parser in Swift seemed like a great idea.

Passkey Sign In with Elixir and Phoenix

Passkeys are a replacement for passwords that use public/private cryptographic key pairs for login in a way that can be more user-friendly and resistant to a number of kinds of attacks. Let’s implement account registration and login with passkeys in a simple Phoenix web app.

This work is heavily based on this article by Adam Langley, which provides a great deal of information about implementing passkeys. My goal here is to fill in some more of the details, and provide some Elixir-specific information. As with Adam’s article, I’m not going to use any WebAuthn libraries (even though that may be advisable from a security/maintenance perspective) since I think it’s interesting and helpful to understand how things actually work.

Providing an exhaustive, production-ready implementation is a non-goal of this post. I’m going to make some slightly odd decisions for pedagogical reasons, and leave some things incomplete. That said, I’ll try to note when I’m doing so.

Theming iOS Apps is No Longer Hard

Well, at least not for the same reasons. I figured I’d write a brief follow-up post, but unless you’ve been living under a rock, you’ll have heard that UIKit gained support for custom traits in UITraitCollection with iOS 17. They work very similarly to SwiftUI’s environment and are exactly what I wanted.

A Hero View Controller Transition in SwiftUI

Out of the box, SwiftUI has a matchedGeometryEffect modifier that makes it relatively easy to build hero transitions (a style of transition where a new screen is presented and part of the source screen changes position and size to reach it’s place on the new screen). It’s cool that SwiftUI includes this out of the box, but unfortunately it has a few limitations that make it unsuitable for certain use cases. Particularly for me, that it doesn’t work with presenting another view. Most examples on the internet[1] work around this by faking a custom presented view: just slap a full-screen background color down and show your content on top of it. That’s essentially the same as a presenting a full-screen view, with the one major caveat that the pseudo-presented view can only cover the containing hosting controller. And if that controller isn’t full-screen (say, if it’s presented as a sheet), you can’t present anything that’s truly full-screen. So, let’s build a custom hero transition that actually presents it’s content across the entire screen.

Portable Identity for ActivityPub

Bluesky has been making waves recently, both on its own merits and how it contrasts implementation-wise with ActivityPub/the fedivese. There are a bunch of ways it differs from other implementations of decentralized social media, but there’s one in particular I want to focus on: portable identity. The idea that I should be able to take all of my data, everything that my identity consists of, and move wholesale to a new instance/server/PDS/whatever you call it. Bluesky permits this[1]. Mastodon and most (all?) other ActivityPub implementations do not. But that doesn’t have to be the case, nothing about ActivityPub—architecturally speaking—is incompatible with this vision.