BLOGS
Wordjunk
I started a project I’m calling WordJunk. On of the inspirations is Merlin Mann’s Wisdom Project. Listening to Reconcilable Differences, I was inspired by the sheer joy Merlin has been getting with this project. The nuggets of wisdom are really fun to read and most bring a smile ot my face.
With WordJunk, I wondered what I can do with this information? A lot? A little? In any case, the wisdom nuggets are something interesting to read while I’m debugging or reading developer documentation. My first task is to show a list with each wisdom item as it’s own row
I call this WordJunk because it reminds me that my goal isn’t to make something perfect. Also, I love the term “Chartjunk”.
Parsing Markdown
The first job is get the markdown in a form that I can do something with. Not all the data is useful or something to render. I’ve been curious about Apple’s swift-markdown package for a while and this might be an interesting job for this project.
First, I saved the the file to a markdown document that I build into the app. I could have read it from the internet But I’m trying to keep this small and focused. Maybe down the road I’ll do that, but I don’t want to distract.
The second thing I’m doing is importing swift-markdown. I know there are a lot of libraries out there to parse markdown. I chose Apple’s library because sometimes the projects Apple creates end up being more adopted than others. It’s still an early project so the documentation wasn’t as robust as I’d have liked, but there was just enough to get me started.
I also relied on Markdownosaur, by Christian Selig, author of Apollo. It’s a cool little project to turn markdown into an NSAttributedString
. I definitely recommend folks take a look at it.
I looked at Merlin’s markdown structure to separate the wisdom snippets from everything else. My basic determination was to use each unordered list, and the corresponding list item was a wisdom snippet. The only caveat is that the first unordered list is the equivalent of a EULA. Some day I might work on figuring out how to address that.
What ended up working best for me was to create a MarkdownWalker
that only matches on UnorderedList
and for each of the listItems
it formats the children using thhe format()
method. It’s a very simple function, but it seems to work well with the Wisdom markdown without too many issues:
struct ListBuilder: MarkupWalker {
var quotes = [String]()
mutating func visitUnorderedList(_ unorderedList: UnorderedList) {
for item in unorderedList.listItems {
let string = item.children.reduce("") { partialResult, markup in
partialResult + markup.format()
}
quotes.append(string.trimmingCharacters(in: .whitespacesAndNewlines))
}
}
}
Rich Text
Because the wisdom document makes good use of rich text in Markdown, I wanted to support this as well. I waffled between using Markdownosaur vs. the Swift native AttributedString
support. I decided to lean on the Swift AttributedString
support because I was not going to be supporting anything other than iOS 15 and it works well with the native Text()
element in SwiftUI.
A caveat with creating an AttributedString from markdown is that it could fail. Since I’m working at the ViewLayer to parse the markdown, I decided to leave sample text in the case that the markdown failed parsing:
private func attributedText(markdown: String) -> Text {
do {
let attributedString = try AttributedString(markdown: markdown)
return Text(attributedString)
} catch {
return Text("Cannot Parse String")
}
}
What’s next
Overall, I’m fairly happy with how this turned out with minimum effort. One nice thing (visible in the screenshot) is that it supports markdown links. Tapping on that blue text opens the browser. It also supports bold, italic, both, and monospaced text.
Where do I go from here? I have a couple ideas including creating a widget, saving favorites, reading form the online repo, using other sources. I might do some of those, or none of those. I’m treating this as a playground, not work. Borrowing from Merlin’s thoughts on the wisdom project: I’m planning on playing in the space while it’s still fun.