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 submit
s 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.
Thanks John for another very informative tutorial!
Thanks Mike.