I finally found the time to test server-side Swift by setting up a Vapor backend. This is something I’ve wanted to test for some time but before swift 3.0 was released it seemed a little bit too much work. Most of the frameworks were also in work in progress state which made things difficult. I chose Vapor since it has the best documentation and seems to be the most popular. Also, Ray Wenderlich is tweeting tutorials for Vapor so I think Vapor has a bright future ahead. You might also want to check Perfect, Kitura and Zewo to get a better view on the server side swift field.
You can download the source code for this example from here: https://github.com/JussiSuojanen/friendservice
TIME TO GET OUR HANDS DIRTY
After Vapor is installed open terminal and create a project called FriendService:

Next we can test if the project was created successfully:
|
cd FriendService Jussis-MacBook-Pro-2:FriendService jussisuojanen$ vapor build Fetching Dependencies [Done] Building Project [Done] Jussis-MacBook-Pro-2:FriendService jussisuojanen$ vapor run serve Running FriendService... No preparations. Server 'default' starting at 0.0.0.0:8080 |
If you see Server ‘default’ starting at 0.0.0.0:8080 you can open your browser and type localhost:8080 and you should see this page:
Next hit ctrl + c (kills the running server) and then type:
Vapor will create an Xcode project for you and you can type ‘y’ in terminal to open it. When Xcode opens the first thing you notice is that there is a lot stuff in the ‘Sources’ -folder. At the bottom, you will see the ‘App’ -folder where we will put all our code. You can also run the application from Xcode but make sure you select the app target before hitting the run button.
First open the main.swift class. Main.swift is the place where you create your Droplet. Droplet is the class that handles all http-requests, routing and setting up the web server. First let’s get rid of all the generated code and try something our own:
|
import Vapor let droplet = Droplet() droplet.get("hello") { request in return "Hello Friend!" } droplet.run() |
Since Vapor has made String to conform to Response protocol we can easily just return a string in our get-request. Now when you build the project and type localhost:8080/hello in your browser. You will see the “Hello Friend!” printed.
MODEL & DATABASE
Next, we will concentrate on our Friends model and the database where it stores all its data. Let’s create a Friend-class under the Models folder. Click on the Models folder to highlight it and then select “File”-> ”New”-> ”File” and select “Swift file”. Give it a name “Friend”. Now there is a bug in the current version of Vapor so you need to close your Xcode and run “vapor xcode” in terminal to open it again so that the project compiles.
First we need to make our Friend to conform protocol Model. By doing that we need to add few variables and functions to our class:
|
id: Node? exists: Bool = false let firstname: String let lastname: String let phonenumber: String |
Node: implies that your model can be converted to alternative representations such as JSON and values that can be stored to database.
Exists: will tell if our value was fetched from the database or if it was just created using the convenience init method.
Rest of the variables are the information we want to store from our friends.
With Model, we also need to conform to JSONRepresentable so we need to implement makeNode-function:
|
func makeNode(context: Context) throws -> Node { return try Node(node: [ "id": id, "firstname": firstname, "lastname": lastname, "phonenumber": phonenumber ]) } |
Last thing we need to for the protocol point of view is to conform to Preparation protocol. Create an extension which implements ‘prepare’ & ‘revert’ functions:
|
/// Prepare the database. Creates the database when used for the first time. static func prepare(_ database: Database) throws { try database.create("friends") { friends in friends.id() friends.string("firstname") friends.string("lastname") friends.string("phonenumber") } } /// Reverts the database (DROP TABLE) static func revert(_ database: Database) throws { try database.delete("friends") } |
Prepare is called when database is used for the first time and it sets up the table for the model.
Revert basically means DROP TABLE -command.
After all this is added our Friend class should look something like this:

Next we will setup our model for the Droplet and add the database provider. Open the main.swift folder and add these lines to the Droplet initializer:
|
import VaporMySQL Droplet(preparations: [Friend.self], providers: [VaporMySQL.Provider.self]) |
We get an error from MySQL-provider since we haven’t yet added it to our project. Lets do that. Open Package.swift and add the provider like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
import PackageDescription let package = Package( name: "FriendService", dependencies: [ .Package(url: "https://github.com/vapor/vapor.git", majorVersion: 1, minor: 1), // mysql provider .Package(url: "https://github.com/vapor/mysql-provider.git", majorVersion: 1, minor: 0) ], exclude: [ "Config", "Database", "Localization", "Public", "Resources", "Tests", ] ) |
You also need to add mysql.json configuration file under the Config-folder you find in your Xcode project. Put this configuration in it:
|
{ "host": "localhost", "user": "root", "password": "", "database": “friends" } |
Make sure the syntax is correct. Otherwise you will get an error like below when preparing your database if something goes wrong.
|
Jussis-MacBook-Pro-2:FriendService jussisuojanen$ vapor run prepare Running FriendService... Could not initialize provider Provider: noMySQLConfig Can not run preparations, droplet has no database Error: Run failed. |
Close project and run ‘vapor xcode’ to fetch the dependencies. Now when you run the project you’ll notice that it does not work. (huh?)
Set up MySQL
First you need to install MySQL to your local machine. With Mac, the easiest way to do so is to use homebrew. Go to
http://brew.sh for more information. You can install home-brew using this command:
|
/usr/bin/ruby -e "$(curl -fsSL <a href="https://raw.githubusercontent.com/Homebrew/install/master/install">https://raw.githubusercontent.com/Homebrew/install/master/install</a>)" |
Next you can install mysql by typing:
Start mysql server:
Connect to mysql server running at localhost:
|
mysql -h 127.0.0.1 -u root |
Create database called Friends:
Verify friends database is created by typing:
Now you can exit and close the connection.
Next lets run ‘vapor run prepare’:
|
Jussis-MacBook-Pro-2:FriendService jussisuojanen$ vapor run prepare Running FriendService... Preparing Friend Prepared Friend Database prepared |
If you get the printed output, it means your friends table is now set up and ready to use. You can verify this from terminal by connecting to mysql server again:
|
mysql -h 127.0.0.1 -u root mysql> use friends; mysql> show tables; mysql> use friends Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -ADatabase changed mysql> show tables; +-------------------+ | Tables_in_friends | +-------------------+ | fluent | | friends | +-------------------+ 2 rows in set (0.00 sec) |
Fluent is a swift ORM Vapor uses to map data between database (MySQL, SQLite, PostQreSQL, Mongo) and objects. The first time you run prepare vapor also creates the ‘fluent’ -table seen in your list of tables.
Now we have our Models and database ready. Next thing to do is to have an API we can use to GET information from our database and to POST information to our database.
SETTING UP THE REST-API
Now that we have our database up and running lets get ready to post some content in it. Open main.swift and add the following method:
|
droplet.post("addFriend") { request in guard let firstname = request.data["firstname"]?.string, let lastname = request.data["lastname"]?.string, let phonenumber = request.data["phonenumber"]?.string else { throw Abort.badRequest } var friend = Friend(firstname: firstname, lastname: lastname, phonenumber: phonenumber) try friend.save() return friend } |
The code above sets up a route addFriend which needs all parameters firstname, lastname, phonenumber to be present to store the data to database. Here we use the Friend initializer method to create the data and to the store it to database by calling the save-method. We also return the friend to provide some output for the request.
Now lets try this out. Select run from Xcode and wait until the service is running. Choose a REST-client to post content to our new service. My favorite tool for the job is Postman:
As you can see we posted some information and the server responded by returning the newly created friend. As we are sending the information as urlencoded you might want to check from the headers tap in Postman that “Content-Type” is set for “application/x-www-form-urlencoded” incase you have any problems.
We also want to list all our friends. That’s easy! Lets create another endpoint called “listFriends”:
|
droplet.get("listFriends") { request in return try Friend.all().makeNode().converted(to: JSON.self) } |
Because our Friend class conforms to Model protocol we can call method all() on friends which fetches all the data from our database. Then we use makeNode() to convert it to JSON. Now lets run the project again and open our browser. Type: “localhost:8080/listFriends” to the address line and see all the users which were successfully posted:
Now that is pretty much what we need for a simple Rest-API but there is still something we can do to keep the main.swift nice and tidy.
CONTROLLERS
Controllers helps you keep your code in order. We will next create a FriendController which will control our data. Start by adding a new file the same way we have done before. Name the file FriendController.
First we will make our FriendController to conform protocol ResourceRespresetable to make it a restful resource. We need to implement method makeResource() to conform the protocol:
|
func makeResource() -> Resource { return Resource(index: index, store: create) } |
Make resource returns a resource called Friend. Inside the method, we define index & create which we will use to access the resources. Before we do that lets make extension for Resource so we can create a new Friend easily from JSON:
|
extension Request { func friend() throws -> Friend { guard let json = json else { throw Abort.badRequest } return try Friend(node: json) } } |
The helper method assumes that we are sending the data in JSON format so next time we send data using Postman the “Content-type” header parameter needs to be set for “application/json” and the data needs to be in JSON-format. In postman, you can send JSON choosing the raw tab under the Body tap and the data format should look like this:

Now after this is done lets create the index and create methods. Let’s copy-paste the code inside the “listFriends” function to the index-function since it does the exact same thing. Inside the create method we’ll use Request extensions friend()-function to create the new friend and we manage with less lines of code.
|
func index(request: Request) throws -> ResponseRepresentable { return try Friend.all().makeNode().converted(to: JSON.self) } func create(request: Request) throws -> ResponseRepresentable { var friend = try request.friend() try friend.save() return friend } |
At this point we also want to add two new functions to our controller: update & delete. Those will come handy in later blog post when we are creating a client app for our backend.
|
func delete(request: Request, friend: Friend) throws -> ResponseRepresentable { try friend.delete() return JSON([:]) } func update(request: Request, friend: Friend) throws -> ResponseRepresentable { let newInfo = try request.friend() var friend = friend friend.firstname = newInfo.firstname friend.lastname = newInfo.lastname friend.phonenumber = newInfo.phonenumber try friend.save() return friend } |
As you can see update and delete function also takes a friend we are editing as a parameter so the method signature is a bit different from the first two functions we wrote. Second parameter is the Friend we want to fetch from the database. Vapor does this on the background by matching the id in the url on the items in the database. If no matching item is found the route fails. Inside the delete we simply call delete for our unwanted friend and he is out! In update function we have a bit more work. We fetch the new information from our request and update that information for the friend we are editing. After that is done we simply call save and return the edited friend. Now our FriendController should look like this:

Now that our FriendController is ready let’s open the main.swift again. The final step is to clear the main.swift file from the add and list functions, create FriendController and add “addFriend”, “listFriends” and “editFriend” as resource with our newly created friendsController as parameter to our droplet:
|
import Vapor import VaporMySQL import HTTP let droplet = Droplet(preparations: [Friend.self], providers: [VaporMySQL.Provider.self]) let friendsController = FriendController() droplet.resource("addFriend", friendsController) droplet.resource("listFriends", friendsController) droplet.resource("editFriend", friendsController) droplet.run() |
Now if you build and run it you’ll see that our listFriend and addFriend functions works the same way as previously. But now our main.swift has only few lines of code which in the long run is a great thing. Imagine a few more endpoints in there responding to a few more models. Pretty soon the code gets messy and will be very hard to maintain.
We still haven’t test the new update and delete functions so lets do that right away. We know that Arnold had to change his phonenumber after quitting his job as the governor so lets update his number using postman:
We created a new command called updateFriend and selected the patch http method. We can use the url: “http://0.0.0.0:8080/editFriend/1” and set the id of the friend we are editing as the last part of the url. And after we hit send button we can see server returned the updated data.
Ok, next lets delete a friend. We remember that Stone Cold Steve Austin was really getting on everyone’s nerves at the company xmasparty last time so we don’t want anything to do with him. (the ‘beer bashing’ really isn’t working out side the ring..)
We created another command which uses the same url as before but instead of the http patch we use the delete method. After the send button is hit Steve is no longer a member in our trusted list of friends!
That is all that I wanted to cover in this post and now you now how to create your own simple restful service with Vapor. In the
next post we’ll create a mobile client that uses this new backend we just created.