Swift, Nginx, Perfect, & PerfectMustache

Posted by on Mar 07 2020, in kraxn

This is a quick start guide on getting started with Swift and the Perfect framework. In a previous guide, we showed how to run Swift Perfect and Nginx together: Perfect handles the dynamic code, and Nginx handles the static files.

Today we are going to use the templating system within Perfect, called PerfectMustache. This is a more involved guide that has a lot of moving parts. If unfamiliar with templating systems, watch [Ray Wenderlich’s excellent introduction to PerfectMustache][1]). Its a great back up if you are ever stuck on anything here! Also check out [Perfect’s perfect blog template][2]).

One thing to note – I mostly just show the code, which very little explanation. I encourage you to fill in the gaps. This is mostly to try to keep things moving along. Check out Swift’s documentation on sections you don’t understand.

Will create 2 very basic webpages – the first will loop through names of birds, the second will display more info on the bird.

394-5685818

395-3638661

Here’s a summary of steps to accomplish this task:

  1. Install PerfectMustache
  2. Create Mustache Templates & static files
  3. Set up Swift Template
  4. Create PerfectMustache Functions
  5. Create Handlers for our pages
  6. Create a new routes
  7. Configure Nginx to handle Mustache So put on your favourite music and grab your favourite beverage – let’s do this 🙂

Install PerfectMustache

Check the instructions at [Github](https://github.com/PerfectlySoft/Perfect-Mustache) if you run into any issues. Within your PerfectTemplate folder you should see a Package.swift file, open this and add the dependency and target configuration.

  • Dependency: .package(url: “https://github.com/PerfectlySoft/Perfect-Mustache.git”, from: “3.0.0”)
  • Target Configuration: .target(name: “PerfectTemplate”, dependencies: [“PerfectHTTPServer”, “PerfectMustache”])

Your Package.swift should look something like this:

// swift-tools-version:4.2

import PackageDescription


let package = Package(
    name: “PerfectTemplate”,
    products: [
        .executable(name: “PerfectTemplate”, targets: [“PerfectTemplate”])
    ],
    dependencies: [
        .package(url: “https://github.com/PerfectlySoft/Perfect-

    HTTPServer.git, from: “3.0.0”),
        .package(url: “https://github.com/PerfectlySoft/Perfect-Mustache.git”, from: “3.0.0”),
    ],
    targets: [
        .target(name: “PerfectTemplate”, dependencies: [“PerfectHTTPServer”, “PerfectMustache”])
    ]
)

Create Mustache Templates & static files

In our last tutorial, we create a virtual directory on our local machine at /var/www/swift.local/html. You can download the [base files from my site](https://kraxn.io/files/perfectmustache+images.tar.gz), it contains the follow:

  1. mustache folder containing index.mustache, and style.mustache. These don’t have mustache markup yet, we are going to write them in.
  2. images folder containing sparrow.jpg, starling.jpg, swallow.jpg, swift.jpg.

Just extract the perfectmustache+images.tar.gz directly into the html folder.

Set up Swift Template.

We will be using the template from the last Perfect/Nginx tutorial: We before we get to the guts of Mustaching, Routing and Handling, we have to set up few things:

  • Add the PerfectMustache Library: import PerfectMustache
  • Add the path to document root: we will use this path to access the mustache templates as an immutable variable.
  • Add a data dictionary – This is an array containing dictionaries of birds. The image files you added earlier are needed for this.
  • Delete the contents of func handler. We will be working on later.

This is our updated swift.main:

import PerfectHTTP
import PerfectHTTPServer
import PerfectMustache

  //our document root<
  let docRoot = “/var/www/swift.local/html/”


  //our bird data
  var birds = [

  [<a href="">name</a>“sparrow”, <a href="">family</a>“Passeridae”, <a href="">wiki</a>“https://en.wikipedia.org/wiki/Old_World_sparrow”, <a href="">image</a>“sparrow.jpg”, <a href="https://kraxn.io/true">progLang</a>,

  [<a href="">name</a>“starling”, <a href="">family</a>“Sturnidae”, <a href="">wiki</a>“https://en.wikipedia.org/wiki/Starling”, <a href="">image</a>“starling.jpg”, <a href="https://kraxn.io/false">progLang</a>,

  [<a href="">name</a>“swallow”, <a href="">family</a>“Hirundinidae”, <a href="">wiki</a>“https://en.wikipedia.org/wiki/Swallow”, <a href="">image</a>“swallow.jpg”, <a href="https://kraxn.io/false">progLang</a>,

  [<a href="">name</a>“swift”, <a href="">family</a>“Apodidae”, <a href="">wiki</a>“https://en.wikipedia.org/wiki/Swift”, <a href="">image</a>“swift.jpg”, <a href="https://kraxn.io/true">progLang</a>,

  ]

  func handler(request:HTTPRequest, response:HTTPResponse) {
  //swift code arriving soon!
  }

  var routes = Routes()
  routes.add(method: .get, uri: “/”, handler: handler)

  try HTTPServer.launch(name: “localhost”,
    port: 8181,
    routes: routes,
    responseFilters: [(PerfectHTTPServer.HTTPFilter.contentCompression(data: [:]), HTTPFilterPriority.high)])

Perfect Mustache Function

Here is a stuct containing the function for interlacing Swift code and mustache template code. This code was taken verbatim from [Ray Wenderlich’s youtube video on PerfectMustache](https://www.youtube.com/watch?v=V98kYKD_R7s).

struct MustacheHelper: MustachePageHandler{


  var values: MustacheEvaluationContext.MapType

  func extendValuesForResponse(context contxt: MustacheWebEvaluationContext, collector: MustacheEvaluationOutputCollector) {

  contxt.extendValues(with: values)


do {
    try contxt.requestCompleted(withCollector: collector)
      } catch {
        let response = contxt.webResponse
        response.appendBody(string: “(error)”)
        .completed(status: .internalServerError)
        }
    }
}

Let’s add this to main.swift:

import PerfectHTTP
import PerfectHTTPServer
import PerfectMustache


  //our document root
  let docRoot = “/var/www/swift.local/html/”

  //our bird data
  var birds = [

  [<a href="">name</a>“sparrow”, <a href="">family</a>“Passeridae”, <a href="">wiki</a>“https://en.wikipedia.org/wiki/Old_World_sparrow”, <a href="">image</a>“sparrow.jpg”, <a href="https://kraxn.io/true">progLang</a>,

  [<a href="">name</a>“starling”, <a href="">family</a>“Sturnidae”, <a href="">wiki</a>“https://en.wikipedia.org/wiki/Starling”, <a href="">image</a>“starling.jpg”, <a href="https://kraxn.io/false">progLang</a>,

  [<a href="">name</a>“swallow”, <a href="">family</a>“Hirundinidae”, <a href="">wiki</a>“https://en.wikipedia.org/wiki/Swallow”, <a href="">image</a>“swallow.jpg”, <a href="https://kraxn.io/false">progLang</a>,

  [<a href="">name</a>“swift”, <a href="">family</a>“Apodidae”, <a href="">wiki</a>“https://en.wikipedia.org/wiki/Swift”, <a href="">image</a>“swift.jpg”, <a href="https://kraxn.io/true">progLang</a>,

  ]


  //Mustache function
  struct MustacheHelper: MustachePageHandler{

  var values: MustacheEvaluationContext.MapType

  func extendValuesForResponse(context contxt: MustacheWebEvaluationContext, collector: MustacheEvaluationOutputCollector) {

  contxt.extendValues(with: values)

  do {
    try contxt.requestCompleted(withCollector: collector)
      } catch {
        let response = contxt.webResponse
        response.appendBody(string: “(error)”)
        .completed(status: .internalServerError)
        }
    }
  }

  func handler(request: HTTPRequest, response:HTTPResponse) {
  //swift code arriving soon!
  }

  var routes = Routes()
  routes.add(method: .get, uri: “/”, handler: handler)


try 

HTTPServer.launch(name: “localhost”,
    port: 8181,
    routes: routes,
    responseFilters: [(PerfectHTTPServer.HTTPFilter.contentCompression(data: [:]), HTTPFilterPriority.high)])

Modify our default handler and add mustache markup

Let’s change our default handler; this already has a route linked to it which is the root page (or homepage). In summary, we will:

  • map var values so they can squeeze into our mustasche template
  • assign our birds array to the values
  • pass this information to our MustacheHelper. Here it’s important to note that we created the docRoot variable so we can find our mustache files.

Here’s our the handler code:

func handler(request:HTTPRequest, response:HTTPResponse) {

    var values = MustacheEvaluationContext.MapType()
    values[“birds”] = birds


  mustacheRequest(request: request, response: response, handler: MustacheHelper(values: values), templatePath: docRoot+“mustache/index.mustache”)

}

Now that we passed the array to our MustacheHelper, we need to make sure that our index.mustache template receives it. In our our case, we only want to grab the bird name. Here we loop through the birds data, just grabbing the names from our dictionary. We create a new link to /hello{{name}} – we will create this page next. Here’s what it should look like:

    <!doctype html>
    <head>
    <meta charset="utf-8">
    <title>{{name}}</title>
    </head>
    <body>

    <div id="content">
            {{#birds}}
    <div style="padding-bottom: 20px; border-bottom: 1px solid black">
                <h4><a href="/hello/{{name}}">{{name}}</a></h4>
            </div>
            {{/birds}}  
        </div>
    </body>
    </html>

to compile and start the server:

swift build
.build/debug/PerfectTemplate

If everything went according to plan, you should be able to go to your http://swift.local and see this:

394-5685818

Create a new route, handler, and mustache mark up.

Let’s create that info page. First thing we should is create a route that will take us to the page. This will take us to the url ‘hello/{name}’‘, where name will be passed from page in the url.

routes.add(method: .get, uri: "hello/{name}", handler: birdHandler)

This a new handler, which we name birdHandler. Here’s what this does in summary:

  • captures the response “name” with error catching
  • loop through birds array to find the request bird
  • if requested bird is found, return the entire dictionary back to mustache

func birdHandler(request: HTTPRequest, _ response: HTTPResponse) {

   guard let name = request.urlVariables[“name”] else { response.completed(status: .badRequest)    return
    }    

  var values = MustacheEvaluationContext.MapType()

     for item in birds {

       if ( item[“name”] as? String == name )
           { 
               values = item
            }
    }
mustacheRequest(request: request, response: response, handler: MustacheHelper(values: values), templatePath: docRoot+“mustache/index.mustache”)

}

Now, lets do the mustache markup. We are going to be reusing the index page. Here is a summary:

  • Our hello/{{name}} doesn’t have the birds array, so we are going to provide a conditional statement to catch this.
  • We can now populate the page with data from our specific bird.
  • We have a conditional state on progLang to see if our bird name is a programming language or not.
    <!doctype html>
    <head>
    <meta charset="utf-8">

    <p><title>{{name}}</title><br />
    {{> style}} <br />
    </head>
    <body></p>


    <p> <div id="content">
    {{#birds}}
    <div style="padding-bottom: 20px; border-bottom: 1px solid black">
    <h4><a href="/hello/{{name}}">{{name}}</a></h4>
    </div>
    {{/birds}}
    {{^birds}}
    <h2>My bird name is {{name}}</h2>
    <p>Family: {{family}} </p>
    <p>Wiki Link: <a href="{{wiki}}">{{name}} info</a></p>
    <p><img src="/images/{{image}}"> </p>
    <p></p>

    <p>            {{#progLang}}
    {{name}} is also a programming language.
    {{/progLang}}</p>

    <p>             {{^progLang}}
    {{name}} is sadly not a programming language.
    {{/progLang}}</p>


    </p>
    a complete list of <a href="/">birds</a>
    {{/birds}}      
    </div>
    </body>
    </html>

to compile and start the server:

swift build
.build/debug/PerfectTemplate

If everything went according to plan, you should be able to go to your http://swift.local/hello/starling and see this:

395-3638661

Adding ‘mustache’ static files to Nginx

Finally, .mustache files are still served by Perfect server. Let’s move those over to Nginx, since they are static files. Change this line in your /etc/nginx/conf.d/swift.local.conf :

        # serve static files
        location ~ ^/(images|javascript|js|css|mustache|media|static)/  {
          root /var/www/swift.local/html;
          expires 30d;
        }

afterwards test and reload:

sudo nginx -t
sudo systemctl reload nginx

Viola! Here is the final main.swift file for reference:

import PerfectHTTP
import PerfectHTTPServer
import PerfectMustache



let docRoot = “/var/www/swift.local/html/”

var birds = [

[<a href="">name</a>“sparrow”, <a href="">family</a>“Passeridae”, <a href="">wiki</a>“https://en.wikipedia.org/wiki/Old_World_sparrow”, <a href="">image</a>“sparrow.jpg”, <a href="https://kraxn.io/true">progLang</a>,

[<a href="">name</a>“starling”, <a href="">family</a>“Sturnidae”, <a href="">wiki</a>“https://en.wikipedia.org/wiki/Starling”, <a href="">image</a>“starling.jpg”, <a href="https://kraxn.io/false">progLang</a>,

[<a href="">name</a>“swallow”, <a href="">family</a>“Hirundinidae”, <a href="">wiki</a>“https://en.wikipedia.org/wiki/Swallow”, <a href="">image</a>“swallow.jpg”, <a href="https://kraxn.io/false">progLang</a>,

[<a href="">name</a>“swift”, <a href="">family</a>“Apodidae”, <a href="">wiki</a>“https://en.wikipedia.org/wiki/Swift”, <a href="">image</a>“swift.jpg”, <a href="https://kraxn.io/true">progLang</a>,

]

struct MustacheHelper: MustachePageHandler{

var values: MustacheEvaluationContext.MapType

func extendValuesForResponse(context contxt: MustacheWebEvaluationContext, collector: MustacheEvaluationOutputCollector) {
contxt.extendValues(with: values)


  do {
      try contxt.requestCompleted(withCollector: collector)
} catch {




let response = contxt.webResponse
response.appendBody(string: “(error)”)
.completed(status: .internalServerError)

}
}
}



func handler(request:HTTPRequest, response:HTTPResponse) {


var values = MustacheEvaluationContext.MapType()

values[“birds”] = birds

mustacheRequest(request: request, response: response, handler: MustacheHelper(values: values), templatePath: docRoot+“mustache/index.mustache”)

}

func birdHandler(request:HTTPRequest, _ response:HTTPResponse) {


guard let name = request.urlVariables[“name”] else {
response.completed(status: .badRequest)
return




}

var values = MustacheEvaluationContext.MapType()

for item in birds {

if ( item[“name”] as? String == name )

{
 values = item
}

}

mustacheRequest(request: request, response: response, handler: MustacheHelper(values: values), templatePath: docRoot+“mustache/index.mustache”)

}

var routes = Routes()
routes.add(method: .get, uri: “/”, handler: handler)
routes.add(method: .get, uri: “hello/{name}”, handler: birdHandler)       

try HTTPServer.launch(name: “localhost”,

port: 8181,
routes: routes,
responseFilters: [
                 (PerfectHTTPServer.HTTPFilter.contentCompression(data: [:]), HTTPFilterPriority.high)])

Images were taken from wiki commons – here is the attribution

  1. sparrow: Fir0002 / GFDL1.2 (http://www.gnu.org/licenses/old-licenses/fdl-1.2.html)
  2. starling: Tim Felce (Airwolfhound) / CC BY-SA (https://creativecommons.org/licenses/by-sa/2.0)
  3. swallow: Axel Strauß / CC BY-SA (http://creativecommons.org/licenses/by-sa/3.0/)
  4. swift: Paweł Kuźniar (Jojo_1, Jojo) / CC BY-SA (http://creativecommons.org/licenses/by-sa/3.0/)