Receiving and Responding to Text Messages with Server Side Swift, Vapor and Twilio

May 29, 2018
Written by
Sam Agnew
Twilion

Screen Shot 2018-05-29 at 7.05.00 PM

You’re building a Vapor app and want to be able to respond to SMS messages? Let’s walk through how to add Twilio SMS to the barebones Vapor “Hello World” app.

Swift Package Manager

For this project we’re going to use Swift Package Manager to set everything up and install dependencies. Make sure you have Swift 4.0 or greater installed before moving on. You can check your Swift version by running the following in your terminal:

swift --version

Vapor has a convenient command line tool which can be used to generate templates for projects. Installing this will also take care of other necessary dependencies.

You can install it using Homebrew with the following command:

brew tap vapor/homebrew-tap
brew install vapor

Now initiate a new Swift project with the following terminal command in the directory where you want your code to live:

swift package init --type executable

This will generate a directory structure for your Swift project. By default it will name your project after the directory you ran the previous command in. My project was named Hello, so you may need to replace that in any of the commands or code further in this post that refer to your Swift project.

Next let’s get our dependencies set up. Open the file Package.swift and add the Vapor package to the code Swift Package Manager generated. My Package.swift looks like this:

// swift-tools-version:4.0
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "Hello",
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        // .package(url: /* package url */, from: "1.0.0"),
        .package(url: "https://github.com/vapor/vapor.git", from: "3.0.0-rc")
    ],
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages which this package depends on.
        .target(
            name: "Hello",
            dependencies: ["Vapor"]),
    ]
)

Getting started with Vapor

We’re now ready to move on to writing some Swift code. From the base directory of your project, open the file Sources/Hello/main.swift and add the following code to it:

import Vapor

let app = try Application()
let router = try app.make(Router.self)

router.get("/") { req in
    return "Hello, world."
}

try app.run()

This is the bare bones “Hello World” Vapor application. It has one route that takes a GET request and responds with a simple string. With your Package.swift configured and your Vapor code written, run the following commands in the terminal to build and run the project:

swift build
./.build/debug/Hello

This will install Vapor and all of its dependencies and will run your web application. Once it is running, visit http://localhost:8080/ to see a basic “Hello World” webpage.

Setting up your Twilio account

Before being able to respond to messages, you’ll need a Twilio phone number. You can buy a phone number here (you can get one for free).

Your Vapor app will need to be visible from the Internet in order for Twilio to send requests to it. We will use ngrok for this, which you’ll need to install if you don’t have it. In your terminal run the following command:

ngrok http 8080

If you’ve just installed ngrok and that previous command didn’t work, you might have to run it like ./ngrok http 8080 from the directory that the ngrok executable is in.

This provides us with a publicly accessible URL to the Vapor application. Configure your phone number as seen in this image by adding your ngrok URL with /sms appended to it to the “Messaging” section:

You are now ready to receive a text message to your new Twilio number.

Adding SMS to your Vapor app

Now that you have a Twilio number you want to allow users to send a text message to it and get a response. The next step is to handle the POST request Twilio will send when your number receives a text.

We only need one route on this app: /sms to handle incoming text messages. Let’s add another route. Replace all of the code in main.swift with the following:

import Vapor

let app = try Application()
let router = try app.make(Router.self)

router.post("sms")  { req -> HTTPResponse in
    // Declare a multi-line string literal of some TwiML to respond with.
    let twiml = """
    <?xml version="1.0" encoding="UTF-8"?>
    <Response>
        <Message>
            <Body>Check out this cool programmatically sent text message!</Body>
        </Message>
    </Response>
    """

    // Create an HTTP Response with the twiml as the body.
    var response = HTTPResponse(status: .ok, body: twiml)
    response.contentType = .xml
    return response
}

try app.run()

In the new sms route we’re creating a string for the TwiML, and then putting it in an HTTPResponse object with a content type of XML. The Body tag in the XML string contains the text of the message we are responding with.

Build and run your server code again and try sending a message to your Twilio number!

What just happened?

With Vapor running on port 8080, sitting behind a public ngrok URL, Twilio can see your application. Upon receiving a text message:

  1. Twilio will send a POST request to /sms.
  2. The function associated with the sms route will be called.
  3. This function responds to Twilio’s request with TwiML telling Twilio to send a message back in response.

If you want to do more with Swift and Twilio, you can check out these other posts:

If you have any questions or other cool ideas, feel free to reach out and let me know: