Saturday, August 13, 2022
HomeiOS DevelopmentFind out how to write HTML in Swift?

Find out how to write HTML in Swift?


This tutorial is all about rendering HTML docs utilizing a model new DSL library known as SwiftHtml and the Vapor internet framework.

Vapor


Introducing SwiftHtml


This time we will begin every part from scratch. Within the first part of this text I’ll present you find out how to setup the SwiftHtml as a bundle dependency and find out how to generate HTML output primarily based on a template file. Let’s begin by making a model new executable Swift bundle.



mkdir Instance
cd "$_"
swift bundle init --type=executable
open Package deal.swift


You can even begin with a macOS Command Line Software from Xcode if you want, however these days I want Swift Packages. Anyway, we should always add SwiftHtml as a dependency to our bundle instantly.



import PackageDescription

let bundle = Package deal(
    title: "Instance",
    platforms: [
        .macOS(.v12)
    ],
    dependencies: [
        .package(url: "https://github.com/binarybirds/swift-html", from: "1.2.0"),
    ],
    targets: [
        .executableTarget(name: "Example", dependencies: [
            .product(name: "SwiftHtml", package: "swift-html"),
        ]),
        .testTarget(title: "ExampleTests", dependencies: ["Example"]),
    ]
)


All proper, now we’re prepared to jot down some Swift DSL code. We will begin with a extremely primary instance to get to know with SwiftHtml. Within the essential.swift file we should always create a brand new HTML doc, then we will use SwiftHtml’s built-in renderer to print the html supply. πŸ–¨


import SwiftHtml

let doc = Doc(.html) {
    Html {
        Head {
            Title("Howdy, World!")
            
            Meta().charset("utf-8")
            Meta().title(.viewport).content material("width=device-width, initial-scale=1")
        }
        Physique {
            Important {
                Div {
                    H1("Howdy, World!")
                    P("This web page was generated by the SwiftHtml library.")
                }
            }
            .class("container")
        }
    }
}

let html = DocumentRenderer(minify: false, indent: 2).render(doc)
print(html)


As you possibly can see the code is fairly simple, particularly if a bit about HTML. The SwiftHtml library tries to comply with the naming conventions as intently as attainable, so in the event you’ve written HTML earlier than this syntax needs to be very acquainted, besides that you do not have to jot down opening and shutting tags, however we will make the most of the Swift compiler to do the boring repetative duties as an alternative of us.


Since we’re utilizing a site particular language in Swift, the compiler can type-check every part at build-time, this manner it is 100% positive that our HTML code will not have syntax points. After all you possibly can nonetheless make semantic errors, however that is additionally attainable in the event you’re not utilizing a DSL. πŸ˜…


The principle benefit right here is that you simply will not have the ability to mistype or misspell tags, and you do not even have to consider closing tags, however you should utilize outcome builders to assemble the HTML node tree. SwiftHtml makes use of tags and it will construct a tree from them, this manner it’s attainable to effectively render your complete construction with correct indentation or minification whether it is wanted.



The DocumentRenderer object can render a doc, additionally it is attainable to create all kinds of SGML-based doc sorts, as a result of the SwiftHtml bundle comes with an abstraction layer. In case you check out the bundle construction you need to see that contained in the Sources listing there are a number of different directories, the core of the bundle is the SwiftSgml element, which permits builders to create different area particular languages on high of the bottom parts. πŸ€”

For instance, in the event you check out the SwiftRss bundle you will notice that it is a easy extension over the SwiftSgml library. You may subclass the Tag object to create a brand new (area particular) tag with an underlying Node object to characterize a customized merchandise to your doc.


The SwiftSgml library could be very light-weight. The Node struct is a illustration of a given SGML node with a customized kind, title and attributes. The Tag class is all about constructing a hierarchy in between the nodes. The Doc struct is a particular object which is accountable for rendering the doctype declaration earlier than the foundation tag if wanted, additionally after all the doc accommodates the foundation tag, which is the start of every part. πŸ˜…


SwiftSgml additionally accommodates the DocumentRenderer and a easy TagBuilder enum, which is a outcome builder and it permits us to outline our construction in a SwiftUI-like fashion.


So the SwiftHtml bundle is only a set of HTML guidelines on high of the SwiftSgml library and it follows the W3C HTML reference guides. You need to use the output string to avoid wasting a HTML file, this manner you possibly can generate static web sites through the use of the SwiftHtml library.


import Basis
import SwiftHtml

let doc = Doc(.html) {
    Html {
        Head {
            Title("Howdy, World!")
            
            Meta().charset("utf-8")
            Meta().title(.viewport).content material("width=device-width, initial-scale=1")
        }
        Physique {
            Important {
                Div {
                    H1("Howdy, World!")
                    P("This web page was generated by the SwiftHtml library.")
                }
            }
            .class("container")
        }
    }
}

do {
    let dir = FileManager.default.homeDirectoryForCurrentUser
    let file = dir.appendingPathComponent("index.html")
    let html = DocumentRenderer(minify: false, indent: 2).render(doc)
    strive html.write(to: file, atomically: true, encoding: .utf8)
}
catch {
    fatalError(error.localizedDescription)
}



This is only one means to make use of SwiftHtml, for my part static web site turbines are advantageous, however the actual enjoyable begins when you possibly can render web sites primarily based on some sort of dynamic information. πŸ™ƒ





Utilizing SwiftHtml with Vapor


Vapor has an official template engine known as Leaf plus the group additionally created a type-safe HTML DSL library known as HTMLKit, so why create one thing very related?


Nicely, I attempted all of the accessible Swift HTML DSL libraries that I used to be capable of finding on GitHub, however I used to be not fully glad with the at present accessible options. Lots of them was outdated, incomplete or I merely did not like the flavour of the DSL. I needed to have a library which is freakin’ light-weight and follows the requirements, that is the rationale why I’ve constructed SwiftHtml. 🀐


How can we combine SwiftHtml with Vapor? Nicely, it is fairly easy, let’s add Vapor as a dependency to our venture first.




import PackageDescription

let bundle = Package deal(
    title: "Instance",
    platforms: [
        .macOS(.v12)
    ],
    dependencies: [
        .package(url: "https://github.com/binarybirds/swift-html", from: "1.2.0"),
        .package(url: "https://github.com/vapor/vapor", from: "4.54.0"),
    ],
    targets: [
        .executableTarget(name: "Example", dependencies: [
            .product(name: "SwiftHtml", package: "swift-html"),
            .product(name: "Vapor", package: "vapor"),
        ]),
        .testTarget(title: "ExampleTests", dependencies: ["Example"]),
    ]
)


We will want a brand new protocol, which we will use assemble a Tag, that is going to characterize a template file, so let’s name it TemplateRepresentable.


import Vapor
import SwiftSgml

public protocol TemplateRepresentable {
    
    @TagBuilder
    func render(_ req: Request) -> Tag
}


Subsequent, we want one thing that may render a template file and return with a Response object, that we will use inside a request handler after we setup the route handlers in Vapor. Since we will return a HTML string, it’s essential to set the correct response headers too.


import Vapor
import SwiftHtml

public struct TemplateRenderer {
    
    var req: Request
    
    init(_ req: Request) {
        self.req = req
    }

    public func renderHtml(_ template: TemplateRepresentable, minify: Bool = false, indent: Int = 4) -> Response {
        let doc = Doc(.html) { template.render(req) }
        let physique = DocumentRenderer(minify: minify, indent: indent).render(doc)
        return Response(standing: .okay, headers: ["content-type": "text/html"], physique: .init(string: physique))
    }
}


Lastly we will lengthen the built-in Request object to return a brand new template renderer if we want it.


import Vapor

public extension Request {
    var templates: TemplateRenderer { .init(self) }
}


Now we simply need to create a HTML template file. I am often making a context object proper subsequent to the template this manner I am going to have the ability to cross round contextual variables for every template file. I am fairly proud of this method to date. ☺️


import Vapor
import SwiftHtml


struct IndexContext {
    let title: String
    let message: String
}

struct IndexTemplate: TemplateRepresentable {
    
    let context: IndexContext
    
    init(_ context: IndexContext) {
        self.context = context
    }
    
    func render(_ req: Request) -> Tag {
        Html {
            Head {
                Title(context.title)
                
                Meta().charset("utf-8")
                Meta().title(.viewport).content material("width=device-width, initial-scale=1")
            }
            Physique {
                Important {
                    Div {
                        H1(context.title)
                        P(context.message)
                    }
                }
                .class("container")
            }
        }
    }
}


Lastly we simply have to jot down some boilerplate code to begin up our Vapor internet server, we will use the app occasion and set a get request handler and render our template utilizing the newly created template renderer extension on the Request object.


import Vapor
import SwiftHtml

var env = strive Setting.detect()
strive LoggingSystem.bootstrap(from: &env)
let app = Utility(env)
defer { app.shutdown() }

app.get { req -> Response in
    let template = IndexTemplate(.init(title: "Howdy, World!",
                                    message: "This web page was generated by the SwiftHtml library."))
    
    return req.templates.renderHtml(template)
}

strive app.run()


Kind of that is it, you need to have the ability to run the server and hopefully you need to see the rendered HTML doc in the event you open the localhost:8080 handle utilizing your browser.


Additionally it is attainable to make use of one template inside one other, since you possibly can name the render technique on a template and that template will return a Tag. The fantastic thing about this method is you could compose smaller templates collectively, this manner you possibly can provide you with a pleasant venture construction with reusable HTML templates written fully in Swift. I am more than pleased with this easy answer and looks like, for me, there isn’t a turning again to Leaf or Tau… πŸ€“



RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular