Skip to content

klasik54/swift-html

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SwiftHtml

An awesome Swift HTML DSL library using result builders.

import SwiftHtml let doc = Document(.html) { Html { Head { Title("Hello Swift HTML DSL") Meta().charset("utf-8") Meta().name(.viewport).content("width=device-width, initial-scale=1") Link(rel: .stylesheet).href("./css/style.css") } Body { Main { Div { Section { Img(src: "./images/swift.png", alt: "Swift Logo") .title("Picture of the Swift Logo") H1("Lorem ipsum") .class("red") P("Lorem ipsum dolor sit amet, consectetur adipiscing elit.") .class(["green", "blue"]) .spellcheck(false) } A("Download SwiftHtml now!") .href("https://github.com/binarybirds/swift-html/") .target(.blank) .download() Abbr("WTFPL") .title("Do What The Fuck You Want To Public License") } } .class("container") Script().src("./js/main.js").async() } } } let html = DocumentRenderer(minify: false, indent: 2).render(doc) print(html)

Install

You can simply use SwiftHtml as a dependency via the Swift Package Manager:

.package(url: "https://github.com/binarybirds/swift-html", from: "1.6.0"),

Add the SwiftHtml product from the swift-html package as a dependency to your target:

.product(name: "SwiftHtml", package: "swift-html"),

Import the framework:

import SwiftHtml

That's it.

Creating custom tags

You can define your own custom tags by subclassing the Tag or EmptyTag class.

You can follow the same pattern if you take a look at the core tags.

open class Div: Tag { } // <div></div> - standard tag open class Br: EmptyTag { } // <br> - no closing tag

By default the name of the tag is automatically derived from the class name (lowercased), but you can also create your own tag type & name by overriding the createNode() class function.

open class LastBuildDate: Tag { open override class func createNode() -> Node { Node(type: .standard, name: "lastBuildDate") } } // <lastBuildDate></lastBuildDate> - standard tag with custom name

It is also possible to create tags with altered content or default attributes.

open class Description: Tag { public init(_ contents: String) { super.init() setContents("<![CDATA[" + contents + "]]>") } } // <description><![CDATA[lorem ipsum]]></description> - content wrapped in CDATA open class Rss: Tag { public init(@TagBuilder _ builder: () -> Tag) { super.init(builder()) setAttributes([ .init(key: "version", value: "2.0"), ]) } } // <rss version="2.0">...</rss> - tag with a default attribute

Attribute management

You can set, add or delete the attributes of a given tag.

Leaf("example") // set (override) the current attributes .setAttributes([ .init(key: "a", value: "foo"), .init(key: "b", value: "bar"), .init(key: "c", value: "baz"), ]) // add a new attribute using a key & value .attribute("foo", "example") // add a new flag attribute (without a value) .flagAttribute("bar") // delete an attribute by using a key .deleteAttribute("b") // <leaf a="foo" c="baz" foo="example" bar></leaf>

You can also manage the class atrribute through helper methods.

Span("foo") // set (override) class values .class("a", "b", "c") // add new class values .class(add: ["d", "e", "f"]) // add new class value if the condition is true .class(add: "b", true) /// remove multiple class values .class(remove: ["b", "c", "d"]) /// remove a class value if the condition is true .class(remove: "e", true) // <span class="a f"></span>

You can create your own attribute modifier via an extension.

public extension Guid { func isPermalink(_ value: Bool = true) -> Self { attribute("isPermalink", String(value)) } }

There are other built-in type-safe attribute modifiers available on tags.

Composing tags

You can come up with your own Tag composition system by introducing a new protocol.

protocol TagRepresentable { @TagBuilder func build() -> Tag } struct ListComponent: TagRepresentable { let items: [String] init(_ items: [String]) { self.items = items } func build() -> Tag { Ul { for item in items { Li(item) } } } } let tag = ListComponent(["a", "b", "c"]).build()

This way it is also possible to extend the TagBuilder to support the new protocol.

extension TagBuilder { static func buildExpression(_ expression: Tag) -> Tag { expression } static func buildExpression(_ expression: TagRepresentable) -> Tag { expression.build() } }

Sometimes you'll need extra parameters for the build function, so you have to call the build method by hand.

In those cases it is recommended to introduce a render function instead of using build.

let tag = WebIndexTemplate(ctx) { ListComponent(["a", "b", "c"]) .render(req) } .render(req)

If you want to create a lightweight template engine for the Vapor web framework using SwiftHtml, you can see a working example inside the Feather CMS core repository.

Credits & references

About

An awesome Swift HTML DSL library using result builders.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Swift 99.9%
  • Makefile 0.1%