Swiccup, Round 2

Just a follow up from my previous post: Swiccup.

Sidenote: Swiccup might be a bit of a dumb name… I was thinking about renaming it SwiftyML, but then I saw another project that is similar called SwifTML, so that might be too confusing…

As I mentioned previously, the first round was a bit of prototyping. I spent a bit more time on it today and have come up with a more flushed out implementation and a more streamlined approach, but it does make use of a couple of operators overloads, essentially creating a mini DSL (domain specific language).

Let’s compare, the original:


html |>
body |>
[h1(["class": "welcome"]) |> "Welcome to Swiccup",
div |> "This is only the beginning!",
ol |> (1…4).map() {
li([string("item #\($0)")])
}]

view raw

swiccup2.swift

hosted with ❤ by GitHub

The new version:


html |>
body |> [
h1 <| ["class": "welcome"] <| ["id": "title"] |> "Welcome to Swiccup",
div <| ["id": "message", "class": "important"] |> "This is only the beginning.",
ol |> (1…4).map {
li |> "item #\($0)"
}]

There are a few differences:

  1. There are now two operators: |> and <|
  2. The signature of all of the tag constructs are now uniformed; this greatly reduces the codegen boilerplate and complexity of the system.
  3. String interpolation now works

The |> and <| operators

The |> operator is used to denote a child relationship between two different HtmlElement structs. The left hand side is the parent and the right hand side is the child. This can be a single entity or an array of children.

The <| operator is used to denote attributes that should be assigned to the HtmlElement to its left, hence the arrow pointing left. This allows either a dictionary to be merged into the attributes store for the element. Above is an example of both single entry dictionaries and multiple entry dictionaries being merged in.

Streamlined Method Signature

The signature for each tag is really nice now, it’s simply:

(_ attributes: [String:String] = [:]) -> HtmlElement

Before, I had a nasty mess of things as I was trying to figure out the best call site API feel. The other nice thing about this is that I only need a handful of operator overloads to handle all of the cases too.

String Interpolation

So yeah, to get string conversion to work properly, you need to implement four different protocols:

  1. ExpressibleByStringLiteral
  2. ExpressibleByUnicodeScalarLiteral
  3. ExpressibleByExtendedGraphemeClusterLiteral
  4. ExpressibleByStringInterpolation

Good times. Also, the ExpressibleByStringInterpolation one is a real doozy. It’s also marked as deprecated, but there is no alternative yet until Swift 4, so you’ll have to ignore the warning if you want to “build clean” (e.g. with no warnings). If you don’t implement that last one, you won’t be able to use string interpolation like:

"item #\($0)"

Wrapping Up

I’m going to keep playing with it. Once I get pretty comfortable about the syntax, I’ll post it up to my github account.

Oh… and just to show the comparison of what the code looks like without the operator helpers:


html()
.addChild(child: body()
.addChild(child: h1(["class": "welcome", "id": "title"]).addChild(child: "Welcome to Swiccup"))
.addChild(child: div(["id": "message", "class": "important"]).addChild(child: "This is only the beginning."))
.addChild(child: ol()
.addChild(child: li().addChild(child: "item #1"))
.addChild(child: li().addChild(child: "item #2"))
.addChild(child: li().addChild(child: "item #3"))
.addChild(child: li().addChild(child: "item #4"))))

view raw

extended.swift

hosted with ❤ by GitHub

You know, just the terrifying normal HTML element construction you see in pretty much every OOP language.

UPDATE @ 10:58PM

While the <| is “clever”, I think I like this API usage better:

h1("class", "welcome", "id", "title") |> "Welcome to Swiccup",

To each their own though and your mileage may vary.

Swiccup, Round 2

2 thoughts on “Swiccup, Round 2

  1. Re: Update @ 10:58: instead of using a sequence of string literals, why not use a convenience initializer/function with class and id values (defaulting to nil) to make it look even cleaner and avoid the mistyping of class/id?

    Like

Comments are closed.