Software Development

Writing A Server-Side Program In Swift Using Vapor

Vapor Swift Web
Written by John Woolsey

Introduction

In this tutorial you will learn how to create and run a server-side program written in Swift using the Vapor library. This allows you to serve web pages or other data over the internet. A basic understanding of programming in Swift is expected. If you are new to the Swift language or would just like to refresh your knowledge, please see our Hello World: Writing Your First Swift Program tutorial before proceeding with this one. Detailed versions of all source code used in this tutorial are available on GitHub for your reference.

What Is Needed

  • Linux or macOS Based Computer
  • Xcode (macOS Only)

Background Information

Vapor is a web framework for Swift that provides a beautifully expressive and easy to use foundation for your next website or API. The Vapor documentation site is a good resource for learning the basics of Vapor along with providing valuable reference material. Vapor University is also a good resource for learning Vapor through contributed tutorials, but be careful to look at the version specified next to the tutorial title so that you are learning about the correct Vapor version. Vapor 3 is the latest major version and is quite different from the previous versions. Vapor is designed to work on macOS and Ubuntu. It is known to work on other Linux distributions as well, but sometimes a little tinkering is required. If you need help with something specific, leave a comment below and I, or someone else, can try to answer your question.

My development system is running macOS High Sierra 10.13.6, Xcode 10.1, and Vapor Toolbox 3.1.10. If you are using a different setup, the vast majority of this tutorial should still apply, however, some minor changes may be necessary.

Verifying Swift Installation

Open a terminal window on your system as we will be using the command line for many of our operations.

As Vapor requires Swift for operation, we need to verify the Swift programming environment is already installed on your system. If you have not yet installed Swift, instructions are available in our Hello World: Writing Your First Swift Program tutorial for installing Swift on macOS or general Linux distributions. For Ubuntu, you can simply run the following Advanced Package Tool (APT) command.

% sudo apt-get install swift

The latest major version of Vapor, 3.0, requires Swift 4.1 or greater. Verify your Swift installation is working properly and is the correct version with the following command.

% swift --version

Installing Vapor

We need to install the Vapor Toolbox which is Vapor’s command line interface that provides shortcuts and assistance for common tasks.

On macOS, use the Homebrew package manager to install the toolbox. Please note that if you are installing Homebrew for the first time, a Bourne shell is required for proper execution of the installation script.

% brew install vapor/tap/vapor

During installation, you may get the message that Xcode alone is not sufficient and you also need to install the Xcode command line tools. If so, run the following command before retrying your Vapor Toolbox installation from above again.

% xcode-select --install

On Ubuntu, use the Advanced Package Tool (APT) to install the toolbox.

% eval "$(curl -sL https://apt.vapor.sh)"
% sudo apt-get install vapor

Once installation is completed, verify the Vapor Toolbox installation.

% vapor --version
% vapor --help

Creating Our First Server-Side Program

Create a new Vapor project with the following command.

% vapor new HelloWorld --template=web

We are using the Vapor Web template here which according to the Vapor documentation specifies that we want to create an HTML website with Leaf templates. Leaf is a templating language we can use to build dynamic web pages that accept data from our Swift code.

Change the directory to the newly created HelloWorld project directory.

Load the project’s dependencies, as defined in the Package.swift file, by running the following command.

% vapor update

If you are running on macOS, you will get the following message.

Changes to dependencies usually require Xcode to be regenerated.
Would you like to regenerate your xcode project now?
y/n>

Choose a run method. If you are familiar with Xcode and would like to create an Xcode project then enter y. Otherwise, enter n to continue from the command line. If you don’t want to create an Xcode project now, you can always create one later with the following command.

% vapor xcode

If you entered y to regenerate your Xcode project, you will see the following message once the Xcode project has been generated.

Select the `Run` scheme to run.
Open Xcode project?
y/n>

Enter y to open the new project in Xcode.

Now that our new project is created, it is time to build and run it. If you are using Xcode, select the Run scheme and the My Mac target, then build and run your project. If you are using the command line, run the following commands to build and run your project.

% vapor build
% vapor run

Once the program is running you should see the following, either within the Xcode console or within your terminal window depending on the run method chosen.

Server starting on http://localhost:8080

Enter the above URL in your browser. You should see the Vapor logo with the “IT WORKS” text in the browser page.

You can leave the program running for now as we will continue to use it in the next section.

Reviewing The Vapor Project Structure

If you are running the command line method, open a new terminal window.

Take a peek around the new HelloWorld project directory structure which is described in the Vapor documentation. It provides a good explanation of the various directories and files used within a Vapor project. I recommend you review it before continuing with this tutorial. One obvious omission in the documentation is the Resources directory. This is where the program will look for certain resources.

Now let’s open and review some of those files in more detail.

Resources/Views

This directory contains the Leaf templates used to render your application’s web pages. Both of the default routes (contained in the Routes.swift file and discussed later) used in the Vapor Web template use the Leaf templating language to render their web pages. The Leaf templates used by these routes are located in this directory and are named welcome.leaf and hello.leaf. Both of these templates load (embed) the base.leaf template that contains the boilerplate HTML structure and then adds (set) title and body code needed for their respective functionality.

Sources/Run/main.swift

This file is the entry point of your program and is executed first when your program is run. It calls the app() function, described next, with the current environment. It typically does not need to be modified.

Sources/App/app.swift

This file defines the app() function, invoked from main.swift, that sets up the application’s configuration, environment, services, etc. that are needed and creates and returns a new Application instance. It typically does not need to be modified.

Sources/App/configure.swift

This file defines the configure() function, invoked from app.swift, that configures your application for use. It mainly contains code for registering the various services we plan to use, such as the Leaf template renderer. It should also be used for any other configuration or setup that needs to be done.

Sources/App/boot.swift

This file defines the boot() function, invoked from app.swift, that runs code after your program boots, but before your application starts running. It typically does not need to be modified.

Sources/App/routes.swift

This file defines the routes() function, invoked from configure.swift, that defines the various routes, or URLs, that will be served by your program. This file contains the guts of your program and is the one you will modify most often. Please note that some Vapor templates capitalize (r) the name of this file, however, it should not affect compiling or running of your program.

The Vapor Web template we are using contains two default routes: router.get and router.get("hello", String.parameter). The first is the base, or home page, route where we saw the Vapor logo. The second says hello to the user. You can test the second route by entering http://localhost:8080/hello/user in your browser and replace user with your first name. This user endpoint corresponds to the String.parameter argument that is passed into the function when the route is executed.

Specifically, the base router.get function takes the website request and returns a rendering of the welcome.leaf template as the current web page for display.

Likewise, the router.get("hello", String.parameter) function takes the website request and returns a rendering of the hello.leaf template with the String.parameter argument passed in as the name variable.

Once you are done reviewing the project structure, stop the program. This can be accomplished by either pressing the stop button in Xcode or entering CTRL-C on the command line depending on your run method chosen.

Adding Our Own Route

We will now create new routes to build a page that uses an HTML form to input information provided by the user and respond with a message. The default template’s routes had their full implementation details located within the Routes.swift file. We will instead create a separate controller for our implementation details and point to that controller within the Routes.swift file. This helps keep the Routes.swift file cleaner for when we have to maintain many routes.

Create a new file named form.leaf within the Resources/Views directory with the following contents.

#set("title") { Form Page }

#set("body") {
<h1>Form Page</h1>
<hr>
<form method="post" action="/send">
<p>Name: <input type="text" name="name" /></p>
<p><textarea name="message" rows="4" cols="30" placeholder="Your message"></textarea></p>
<p><input type="submit" /></p>
</form>
<hr>
#(response)
}

#embed("base")

Line 1 sets the title of our new web page.

Lines 3-13 set the body of our web page. Lines 6-10 construct an HTML form that accepts name and message inputs from the user and calls the send route when the user submits the form. Line 12 displays the response that we will send back.

Line 15 loads the base HTML structure contained in the base.leaf template.

Now create a new file named FormPageController.swift within the Sources/App/Controllers directory that contains the following.

import Vapor

final class FormPageController {

   // Variables
   var formName = ""      // form input for name
   var formMessage = ""   // form text box for message
   var formResponse = ""  // form response to send back


   // Routes

   // Displays form page at "form" route
   func displayFormPage(_ req: Request) throws -> Future<View> {
      if !formName.isEmpty {
         if !formMessage.isEmpty {
            formResponse = "\(formName) says '\(formMessage)'."
         } else {
            formResponse = "Hello, \(formName)!"
         }
      } else {
         formResponse = ""
      }
      return try req.view().render("form", [
         "response": formResponse
         ])
   }

   // Submits form page at "send" route and redirects to "form" route
   func submitFormPage(_ req: Request) throws -> Response {
      formName = try req.content.syncGet(at: "name")
      formMessage = try req.content.syncGet(at: "message")
      return req.redirect(to: "/form")
   }
}

Lines 6-8 define our variables: formName and formMessage for our form inputs, and formResponse for our form output.

Lines 14-27 define the displayFormPage() function that will be called when we request the form route. It checks which input variables the user populated, creates an appropriate response, and renders the form.leaf template with the passed in response variable.

Lines 30-34 define the submitFormPage() function that will be called when we request the send route. It retrieves the user’s inputs, saves them to local variables, and then redirects to the initial form route for display to the user.

Next, let’s add the routes for this form page to our Routes.swift file. Add the code shown below just after the existing hello route and before the ending } of the routes() function.

// Form page
let formPageController = FormPageController()
router.get("form", use: formPageController.displayFormPage)
router.post("send", use: formPageController.submitFormPage)

Here we are creating a new FormPageController instance and using different calling mechanisms of the router to get and post our new form and send routes respectively using the functions we defined in the FormPageController class.

Build and run your updated project again. Go to the http://localhost:8080/form route and test out the new form capability by submitting the form with various populated or empty inputs and see the responses.

Once you are done, stop the program by pressing the stop button in Xcode or entering CTRL-C on the command line.

Summary

In this tutorial we learned how to:

  • install Vapor, a server-side Swift library,
  • create and run a new Vapor project using the Vapor Toolbox command line interface, and
  • add our own URL routes for custom functionality.

The final source code for this tutorial is located on GitHub.

Thank you for joining me in this journey and I hope you enjoyed the experience. Please feel free to share your thoughts in the comments section below.

About the author

John Woolsey

John is an electrical engineer who loves science, math, and technology and teaching it to others even more.
 
He knew he wanted to work with technology from an early age, building his first robot when he was in 8th grade. His first computer was a Timex/Sinclair 2068 followed by the Tandy 1000 TL (aka really old stuff).
 
He put himself through college (University of Texas at Austin) by working at Motorola where he worked for many years after that in Research and Development.
 
John started developing mobile app software in 2010 for himself and for other companies. He has also taught programming to kids for summer school and enjoyed years of judging kids science projects at the Austin Energy Regional Science Festival.
 
Electronics, software, and teaching all culminate in his new venture to learn, make, and teach others via the Woolsey Workshop website.

2 Comments

Leave a Comment