15 years ago, I started my first open source project on GitHub. Originally it was called NSAttributedString-Additions-for-HTML but later I renamed it to DTCoreText. I had switched to maintenance mode 8 years ago, because I had no interest in putting more work into an Objective-C code base.
Besides of the old language there were too many dependencies into even more Objective-C frameworks, namely DTFoundation which held the libxml2-based parser which is used by DTCoreText and many workarounds for compiling on older operating systems from a time before ARC.
So for the past decade this book was closed for me. I freely admit that I had found a new love in Swift and it with all the happiness that this provided I just couldn’t reopen it. For a long time I also soothed myself with Apple telling us that nobody is forcing you to migrate to Swift, because Objective-C libraries happily coexist with Swift.
But the truth of the matter is that Swift continued to develop and get pretter every year while Objective-C remained an ugly hack. Towards the end of last year, I needed to parse some HTML for generating markdown from it, for my Swift Agents. That caused me to revisit libxml2’s relaxed HTML parsing mode. This time around I wrapped this C library in Swift and it felt like a breath of fresh air. So that’s why I added it to SwiftText, my collection of Text manipulation utilities.
And this planted the idea at the back of my mind: if coding models have gotten so good, maybe one is up to the task of fully migrating DTCoreText to Swift?
Claude Code Opus 4.6 did it!
The big plus from having a great many unit tests in my OSS projects is that you have an easy way to determine correctness of the migration. If you can give a coding agent a deterministic way to know when it is done, then it can work on a task for hours until it is truly done.
So I followed my hunch and tasked Opus (via Claude Code) to attempt a migration. I had my doubts, but a few hours later all unit tests were passing and even the Demo App – which remains in Objective-C for the first version – is still performing flawless.
So the first migration step was to migrate all the unit tests to Swift Testing. The second was to rip out the prior dependency to DTFoundation with the older HTML parser. And while doing so to also remove support for OS versions that are no longer supported by the compiler. The new parser now uses an asynchronous stream of parsing events instead of delegate calls, as to allow it being pure Swift.
After that we migrated to use native types where previously I had my own. Up until the very end we had an option to turn on UIKit-compatibly attributes which was off by default. That’s now gone as well many more crufty workarounds. Internally the attributed string builder no longer has any GCD-tech, but is completely based on structured concurrency, including an async generation function that supports cancellation.
And as a almost free bonus there’s now also a bridge from NSAttributedString to the SwiftUI value type AttributedString – so you can now use standard text views with the generated strings.
Do we still need DTCoreText?
Well, to be honest I have been asking myself the same question for upwards of 10 years now. There are quite a few things that modern TextKit still doesn’t do.
TextKit only supports a single shadow, loads embedded images only synchronously, doesn’t support images as list bullets, no text boxes and no custom HTML objects. Oh and no custom subviews. And probably a few items more. I don’t know how well TextKit supports CSS inheritance. DTCoreText was always trying to be more faithful to the HTML.
Then there’s the topic of tables. It would probably be not too difficult for a coding agent to implement something. If I remember correctly then the Apple-provided parsing functions did parse HTML tables on Mac. Now that we are not hindered by Objective-C any more and we have coding agents, the world is our oyster!
Conclusion
If you are still stuck with Objective-C, don’t worry, DTCoreText is still callable from there. Internally it’s fully Swift, but the surfaces that might need to be seen from “old code” still exist.
I did close all stale open pull requests on the project in preparation for the migration. Now on Swift it will be much easier to fix issues or implement new functionality since that’s done by agents these days.
Regardless I’d be interested to hear from people who are still actively using DTCoreText in their projects. Because as I said, there might be some legitimate advantages that still exist over Apple-provided frameworks.
The next steps might be to implement a SwiftUI view that supports the additional DTCoreText functionality and possibly migrating the Demo App also to Swift. What else would you like?
Categories: Administrative