Full Stack Swift — Deploying a Vapor app

Daniel Williams
Jun 8, 2016 · 11 min read
Image for post
Image for post

I love Swift. I’m not sure if it’s because of the statically typed nature of the language, or the constantly growing community that are always willing to lend a helping hand. I’d probably lean more towards the latter, in hopes that I can help someone the way that the Vapor community members have helped me understand the wonderful world of web frameworks. Keep in mind that I’m a musician by trade, and this is my first tutorial, but I’m still going to try to explain things as technically proficient as possible. In other words, bear with me here, and if I’m doing something wrong please leave comments. I would always love to learn more.


Prerequisites


From the beginning


Installing Swift with SwiftENV

Installing swiftenv

This should install Swift and set up the “swiftenv” command through your bash profile. You’ll probably need to restart terminal after installing this. Pop terminal back open and type the following command in:

swiftenv install DEVELOPMENT-SNAPSHOT-2016–05–31-a

This one might take a while. It’s going to download the swift development snapshot, which is quite large. After it’s finished installing, give terminal the following command to set your swiftenv settings to the snapshot that you just downloaded:

swiftenv global DEVELOPMENT-SNAPSHOT-2016-05-31-a

As long as swift is in your $PATH (if not, google it or head to swift.org’s installation instructions) you should now be able to check that everything worked with:

swift --version

Welcome, Vapor

To make developers lives easier, Vapor comes with a CLI that will help you create new vapor apps, generate Xcode projects for them, build them, run them, as well as deploy them to Heroku or Docker. How cool?! We can install it with the following commands, one at a time in terminal:

Installing Vapor CLI

Starting a new project

vapor new TodoList

This will generate an example app from github. It puts it all in a folder, initializes a new git repo for us, generates an xcode project, and sets up our entire folder structure very similarly to other web server back-ends such as Ruby on Rails or Node.js/express. Neat! Let’s open the main.swift file that lives in the App directory that was just generated for us. If you’re using atom as your text editor, just type the following commands. Otherwise find and open main.swift in your favorite text editor. It lives in the TodoList/App/ directory that was just generated for you:

cd TodoList/
atom .

Main.swift

Then we’ll need a reference to our application, as well as some configuration. (Configuration is saved in the Config/development/app.json file for future reference). Put this is main.swift as well:

Next we need to give a list keys that mustache will use, with values that route to our .mustache files. By default, it will look in the Resources/Views/ folder:

We’re only going to have two endpoints for this tutorial. One that gets a single Todo, and one that gets all of our Todos. In addition, I’ll show you how to respond differently to JSON requests versus HTML requests. A route needs an endpoint path, and a handler. You can also specify a type safe value to restrict requests to a certain type. We’ll do this as well:

And finally, we end Main.swift by starting the web app:

Here is the completed main.swift file:


TodoController.swift

touch App/Controllers/TodoController.swift

Now that you have an empty TodoController.swift, open that file up in a text editor and add the following imports:

Woah, woah, woah. What’s this MongoKitten thing? This is a driver for mongoDB that will help us talk to mLab.com (which will be hosting our database). It’s extremely easy to install thanks to the Vapor CLI and the Swift Package Manager. All you need to do is open the Package.swift file in your /TodoList/ directory, and add the MongoKitten package to your dependencies as follows:

Please note that you’re changing this in Package.swift, NOT in TodoController.swift.

Let’s hop back over to TodoController.swift. We need to create the class, TodoController and have it conform to Controller. We also need to give it an initializer that fires up references to our application and database, as well as a typealias for the functions that we’ve yet to build. Those are coming later:

Then we define the two functions that we referenced in Main.swift. These are the index function, and the show function. They are the two endpoints that we routed earlier!

We’re going to fill in the rest of the details later. For now, let’s deal with this Database thing that we’ve put in our controller. We have a reference to it, but it doesn’t exist yet. Let’s fix that!


Database.swift

mkdir App/Database
touch App/Database/Database.swift

Open Database.swift and add the imports:

We’ll make a class that contains the Database, with a singleton variable that will let us reference the database (since we need to reference it in TodoController.swift). We also create variables to reference our MongoDB information. We need references to our server, our database, and our collection (which holds our objects/todos/documents/whatever you want to call them). We’ll also add an initializer that will initialize our connection to our mLab.com database. Lastly, we’ll add an enum that will hold our environment variables. Here is the completed Database.swift file:

As you can see, we’re referencing app.config again, so you’ll need to set the correct values in your Config/secrets/app.json file like so:

These values come from your mLab.com account. Create a new deployment from the mLab.com account dashboard. As you can see in the config file, i named my deployment DB name “vapor_todos”:

Image for post
Image for post
Image for post
Image for post
values for Config/secrets/app.json

While you’re here, you’ll need to create a User to access the database through the Users -> Add database user page:

Image for post
Image for post
add a user and use this info in Config/secrets/app.json

Todo.swift

We want to be able to turn this Todo into JSON. This is simple, just conform to the JSONRepresentable protocol. Add this to the end of Todo.swift:

We also need to conform to the StringInitializable protocol to use Type-Safe routing with vapor. This is where we’ll start our string of failable convenience initializers so that we can initialize a Todo via just one param, such as its uniqueID:

The way this works is a bit confusing, but we’ve basically given ourselves a way to initialize a Todo from a document, which is the name that mongoDB gives objects. If we use type safe routing, it’ll use our StringInitializable ‘from string’ init, which will call our ‘fromUniqueId’ init, which will eventually call our ‘document’ init, which in turn calls our designated initializer in the class. Phew. Lastly, we add an extension for Mustache to conform to the MustacheBoxable protocol. This is needed to allow mustache to loop over collections on custom types. It’s very similar to the way we set up makeJson() above.

Here is the finished Todo.swift file:


Finishing TodoController.swift

Now, for the index function we want to list all of our todos, not just one single todo. On top of that, we would like to respond differently depending on if the request wants JSON or HTML, just for fun. So we’ll add a simple if statement to check the headers. We can access the headers through request.headers[“header_key”]. Add this:

When the main.swift file requests the TodoController’s index function, it will find all of the todos in our database via database.todos.find(), and then we’ll map those to into Todo classes. After that, we map those objects into JSON objects, thanks to our Todo conforming to the JSONRepresentable protocol. We then do a small check, if the content type is JSON, we render JSON. Otherwise, we’ll try to render a view with our mustache template, and then pass variables in through its context. Since we passed the “todoList” variable through context, we’ll be able to access it in our mustache files with {{#todoList}}.

Here is the completed TodoController.swift file:


Mustache files

touch Resources/Views/Includes/header.mustache
touch Resources/Views/Includes/footer.mustache
mkdir Resources/Views/Todo/
touch Resources/Views/Todo/index.mustache

Open up header.mustache and put this in it:

Open footer.mustache and put this in it:

Open index.mustache and fill it out:

As you can see, you can pass your included files (listed in main.swift) in using {{>name_here}}. And as I mentioned before, we passed todoList through context into the mustache file, so we can loop through that context thanks to conforming to the MustacheBoxable protocol. You can loop through arrays with “#”, end with “/”, and refer to something with “.” You can do much, much more with mustache and can read more about it here.


Faking Some Data

Image for post
Image for post

Then click the newly created collection and go to “Add document” and add the following code into the document:

Click “Create and Go Back”. Repeat this process for two more todos on mLab:

and


Can we build yet!?

vapor build

Cross your fingers, and on success:

vapor run

We should be up and running now, and able to test our site. Remember that we only have two routes. We have localhost:8080/todos/ and localhost:8080/todos/X, where X is a string that matches the mLab document’s “unique_id” value, because we programmed it that way through our convenience initializers! If you head to localhost:8080/todos, you’ll see our TodoController.index that is running our index.mustache view:

Image for post
Image for post
ignore my address bar

Click one and you’ll be taken to the json response, which equates to our TodoController.show:

Image for post
Image for post
ignore my address bar

Remember how we made sure our TodoController.index displayed differently for Json requests? Lets try that:

JSON will respond back. Nailed it!


Deploying to Heroku

git add -A
git commit -am "todo starter"
vapor init heroku

Accept all of the defaults when asked questions regarding app name, build pack, and pushing — just hit enter on all of them. After a bit of time, you’ll get hit back with an address that goes to your todo starter on Heroku. Congratulations!


Bonus: work in Xcode

vapor xcode

I couldn’t personally get the secrets/app.json to load, but I didn’t spend much time tinkering with it. If someone else has any idea, shoot me a message.


Thanks for the help


Parting Notes

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store