Introducing MarkCodable
I had a lot of fun listening to “A releational database using Markdown” episode with John Sundell and Gui Rambo:
After taking a moment though, I started realizing that there is a whole plethora of use cases where Markdown could be a great storage for some of my data. Markdown supports tables and they are pretty easy to view, parse and edit by hand.
On the other hand, JSON isn’t very easy to view or edit by hand, which is sometimes more important for me, given I work on a lot of small tooling that automates or performs specific tasks and usually doesn’t need a large dataset. But it’s often important to be able to view and edit the data, if needed.
And so, this is how the idea for MarkCodable came to be.
A Markdown Codable
My first thought was to define a semantic markdown spec that could express any kind of nested Codable
structure, much like what JSON does.
But… I do have some similar experience with helping folks to use Swift DocC’s custom Markdown format. Documentation Markdown has just a couple of features on top of the common Markdown format but that’s still a cliff many won’t climb on their own — it’s non-trivial to do that.
So, I decided to focus on the most straight-forward, common use case of encoding simpler, less complex data in a Markdown table. Then, if that picks up, think about a more complex format to handle more advanced use cases.
Encoding a single value
Long story short, MarkCodable
flattens your Codable
structures and plots them into a plain text table as a string. First, you define a Codable
structure or class like usual, for example:
|
|
Then you create some instances and encode them as Markdown:
|
|
And the output you get is valid, deterministic Markdown:
|isSocial|number|price.currency|price.price|street |
|--------|------|--------------|-----------|-------|
|true |239 |EUR |20000.0 |Main St|
When you open this in a Markdown viewer you get a proper table:
Encoding collections
Later on, you can decode the markdown (from a file or memory), modify the data and encode it back as text. For example, let’s decode the markdown that we had so far as an array of houses:
|
|
Then add one more house to the collection:
|
|
And finally, encode it again and see the resulting Markdown:
|
|
This time you get:
|isSocial|number|price.currency|price.price|street |
|--------|------|--------------|-----------|---------|
|true |239 |EUR |20000.0 |Main St |
|false |84 |JPY |230000.0 |Market St|
Simple, isn’t it?
Interoperability with JSONEncoder
One of the benefits of using a Codable
instead of a custom Markdown writer is that you can use MarkEncoder
for human friendly output but still fall back on JSON with JSONEncoder
if you want to send the data to a web API:
|
|
[
{
"number": 239,
"street": "Main St",
"isSocial": true,
"price": {
"price": 20000,
"currency": "EUR"
}
},
{
"number": 84,
"street": "Market St",
"isSocial": false,
"price": {
"price": 230000,
"currency": "JPY"
}
}
]
Working with more data
While, the primary focus of MarkCodable
is to offer humans a trivial plain-text data format, should your markdown grow in size or complexity you can always use a more powerful tool to browse or edit the data. Below, I’ve opened my Markdown file with the TableFlip app to easily add more houses to my listing in a GUI:
Markdown codable use cases
If you already think this is something that might be useful for you, head straight to https://github.com/icanzilb/MarkCodable.
Otherwise, here are just few use cases that might resonate with you if you had to do something similar in the past.
Configuration files
Assume you have an easily editable config file config.md
:
| environment | host | port | user | schema |
|-------------|-----------|------|------|--------|
| qa | 127.0.0.1 | 8080 | test | http |
| production | 2.317.1.2 | 9999 | app | https |
Use MarkDecoder
to get the config you need for a particular run of your app:
|
|
Unit tests data objects
Sometimes you need some extra data to create the system under test in your unit tests. Often it’s too noisy to create all the objects in code or non-trivial if the data structures are still in flux.
You can easily embed and edit Markdown in your Swift code and keep your tests clean. Let’s explore an example — first let’s define a struct called Computer
that we want to test:
|
|
Now we can create as many instances as we want by decoding a simple markdown string in the unit test like so:
|
|
Simple Database
Finally, I think using Markdown as a simple database format is a great choice. You can edit the data in any text editor locally or online on GitHub.
Having your data in plain text format gives you change history via Git, change approvals via GitHub PRs, hosting and authentication, if needed, via GitHub or GitLab.
In fact, I went ahead and put together a simple Markdown-driven SwiftUI app of about 70 lines of code that:
- 🤖 uses GitHub for backend
- 🖼 SwiftUI + MarkCodable for the iPhone app
- 💵 Amazon for payments
Check out the code here: https://github.com/icanzilb/MarkCodingDemoApp
Where to go from here?
I wrote a bit more about the motivation behind MarkCodable
here:
🎉🧵 Today, I'm releasing MarkCodable — a Codable implementation that uses Markdown as storage: https://t.co/YCnknNZGYM
— Marin Todorov (@icanzilb) September 5, 2022
Read below to learn more about my key motivators for this new Swift Package: pic.twitter.com/erwpoI1s1v
Let me know what you think!
Thanks for reading! If you’d like to support me, get my book on Swift Concurrency:
In case you want to talk more, hit me up on twitter at https://twitter.com/icanzilb.