Getting Started with Perfect

While the Perfect team has provided the Perfect Assistant macOS app to get projects up and running quickly, I've opted to build this project from scratch from the command line. Don't get me wrong, I like magic as much as the next dev, but I find it easier to debug software when I understand how it works.

Here are the steps I took to get this site started.

Setting up a Basic HTTP Server

Create a new directory for the project and cd into it.

$ mkdir example.com
$ cd example.com

Initialize a new executable package via the Swift Package Manager.

$ swift package init --type executable

This creates particular file structure which includes Sources/main.swift. This structure seems to determine the configuration when generating Xcode projects which we'll do in a few steps.

Edit the generated Package.swift file to add project dependencies.

/* Package.swift */

import PackageDescription

let package = Package(
    name: "example.com",
    dependencies: [
        .Package(
            url: "https://github.com/PerfectlySoft/Perfect-HTTPServer.git", 
            majorVersion: 2
        )
    ]
)

Create an Xcode project using the Swift Package Manager's generate-xcodeproj command. This fetches all the dependencies and generates an Xcode project with the appropriate structure and configuration.

$ swift package generate-xcodeproj

Open the generated Xcode project and setup an HTTP server running on localhost by replacing the contents of main.swift with the following.

/* main.swift */

import PerfectLib
import PerfectHTTP
import PerfectHTTPServer

// MARK: Configure Routes

let routes = Routes([
    Route(method: .get, uri: "/", handler: { (request, response) in
        response.setBody(string: "Hello, world!")
        response.completed()
    })
])

// MARK: Configure Server

let server = HTTPServer()
server.serverPort = 8181
server.addRoutes(routes)

// MARK: - Run Server

do {
    try server.start()
} catch PerfectError.networkError(let error, let message) {
    print("Network error thrown: \(error) \(message)")
}

Important: Select the executable scheme from the scheme selector in Xcode. It will most likely be the second in the list and not selected by default.

Select Executable Scheme in Xcode

Build and run the project to confirm everything is working and there are no build errors. The first run will take a few moments while all the dependencies are built for the first time.

Navigate to http://localhost:8181/ in a web browser on the same machine to see the results!

Hello, world! printed in web browser

Serving Static Files

Create a webroot directory in the project's root directory with a hello.txt file which will be used to confirm everthing is working.

$ cd <path-to-project-root-directory>
$ mkdir webroot
$ echo "I'm inside the hello.txt file." >> webroot/hello.txt

Regenerate the Xcode project.

$ swift package generate-xcodeproj

There will now be a blue webroot Folder Reference containing the hello.txt file in the project navigator.

Blue webroot folder reference in project navigator

Note: Folder References are references to the underlying file system. Changes to a Folder Reference will propagate to the referenced files and directories in the file system.

Set the Working Directory of the executable scheme in Xcode:

  • Select the executable scheme if not already selected.
  • Select Edit Scheme.
  • Select the Options tab.
  • Set the project root directory containing .xcodeproj as the Working Directory.

Select Edit Scheme

Setting working directory in options tab

Tell the HTTPServer instance in main.swift to serve static files from the newly created webroot directory.

/* main.swift */

...
server.documentRoot = "webroot"
...

Re-run the app and navigate to http://localhost:8181/hello.txt in a web browser on the same machine to see the contents of the file.

Contents of hello.txt served in browser

What's Next?

My next task was to add templating via Perfect-Mustache. I'll be documenting the process in Templating with Perfect-Mustache (Coming Soon). Stay tuned!