Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Hands-On Full-Stack Development with Swift

You're reading from   Hands-On Full-Stack Development with Swift Develop full-stack web and native mobile applications using Swift and Vapor

Arrow left icon
Product type Paperback
Published in Mar 2018
Publisher Packt
ISBN-13 9781788625241
Length 356 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Ankur Patel Ankur Patel
Author Profile Icon Ankur Patel
Ankur Patel
Arrow right icon
View More author details
Toc

Table of Contents (13) Chapters Close

Preface 1. Getting Started with Server Swift 2. Creating the Native App FREE CHAPTER 3. Getting Started with Vapor 4. Configuring Providers, Fluent, and Databases 5. Building a REST API using Vapor 6. Consuming API in App 7. Creating Web Views and Middleware 8. Testing and CI 9. Deploying the App 10. Adding Authentication 11. Building a tvOS App 12. Other Books You May Enjoy

Building a web server in Swift

Now that we have a little background on HTTP and formats for its request and response payloads, let's try to build an HTTP server from scratch using Swift and some C libraries that we can access via Swift. The point of this exercise is to learn how to build a very basic HTTP web server so we have a better understanding of how all of these web servers are built using sockets. Getting a full stack view is helpful in case you need to dive into low-level code to debug an issue or fix a bug in a package or library you might be using to build our web server. You might also be curious on how to code your own simple web servers, it's actually fairly easy. To create a web server, let's try the following steps:

  1. Import the C libraries in Swift:
import Darwin.C
  1. Create a socket using the socket system call. Sockets are a way for other hosts on the network to connect to this process:
let sock = socket(AF_INET, SOCK_STREAM, 0)
  1. Create a socket address structure and initialize it with host and port information. Then call the bind system call with the socket address structure and bind the server to the localhost on the port specified:
bind(sock, sockaddrPtr, socklen_t(socklen))
  1. Listen for incoming requests by calling listen and specifying the max number of requests to be added to the queue to be served by our process:
listen(sock, 5)
  1. Now, we can accept incoming connections by calling accept with the socket file descriptor for our socket that clients connect to. It will remove requests from the listen queue and return a new client socket connection:
let client = accept(sock, nil, nil)
  1. We can read from the new client socket connection and send data to it using HTTP Protocol:
  let html = "<!DOCTYPE html><html><body><h1>Hello from Swift Web Server.</h1></body></html>"
let httpResponse: String = """
HTTP/1.1 200 OK
server: simple-swift-server
content-length: \(html.count)

\(html)
"""
httpResponse.withCString { bytes in
send(client, bytes, Int(strlen(bytes)), 0)
}
  1. Close the connection using the close system call:
close(client)

That was a quick overview of how network-based programs work and how our web server will work as well. Now, let's look at the code as a whole:

import Darwin.C
let zero = Int8(0)
let transportLayerType = SOCK_STREAM // TCP
let internetLayerProtocol = AF_INET // IPv4
let sock = socket(internetLayerProtocol, Int32(transportLayerType), 0)
let portNumber = UInt16(4000)
let socklen = UInt8(socklen_t(MemoryLayout<sockaddr_in>.size))
var serveraddr = sockaddr_in()
serveraddr.sin_family = sa_family_t(AF_INET)
serveraddr.sin_port = in_port_t((portNumber << 8) + (portNumber >> 8))
serveraddr.sin_addr = in_addr(s_addr: in_addr_t(0))
serveraddr.sin_zero = (zero, zero, zero, zero, zero, zero, zero, zero)
withUnsafePointer(to: &serveraddr) { sockaddrInPtr in
let sockaddrPtr = UnsafeRawPointer(sockaddrInPtr).assumingMemoryBound(to: sockaddr.self)
bind(sock, sockaddrPtr, socklen_t(socklen))
}
listen(sock, 5)
print("Server listening on port \(portNumber)")
repeat {
let client = accept(sock, nil, nil)
let html = "<!DOCTYPE html><html><body style='text-align:center;'><h1>Hello from <a href='https://swift.org'>Swift</a> Web Server.</h1></body></html>"
let httpResponse: String = """
HTTP/1.1 200 OK
server: simple-swift-server
content-length: \(html.count)

\(html)
"""
httpResponse.withCString { bytes in
send(client, bytes, Int(strlen(bytes)), 0)
close(client)
}
} while sock > -1

To run our server, let's take look at the following steps:

  1. Create a Swift file and call it simple-server.swift.
  2. Copy the preceding code into the file.
  3. Run the code using the Swift command and pass the file name as the first argument, as follows:
$ swift simple-server.swift
  1. The Swift compiler will try to compile the contents of simple-server.swift and run it in one command. You should see the following printed in the Terminal when the server has started:
Server listening on port 4000
  1. Open the browser and go to http://localhost:4000. You will see the response from our Swift simple HTTP server replying back with HTML content:

The example code works only on macOS but you can easily make it work in Linux by using import Glibc instead of import Darwin.C and changing some of the datatypes that are passed in creation of a socket. Swift supports some of the C directives, such as #if, #elsif, #else, and #endifto help include or skip code blocks before compiling on certain platforms, such as Linux or macOS, where a certain feature or API usage may be different. In our case, since we depend on C-based libraries, we'd need to import Glibc when OS is Linux or import Darwin.C and also set different types for two variables we use in our code, as follows:

#if os(Linux)

import Glibc
let zero = UInt8(0)
let transportLayerType = SOCK_STREAM.rawValue // TCP

#else

import Darwin.C
let zero = Int8(0)
let transportLayerType = SOCK_STREAM // TCP

#endif
Directives are a way to let the compiler know how to process the source code before compiling. There are specific language constructs that let the compiler preprocess the source code and this comes in handy when we want to build a server-side Swift web applications where we need to ignore certain code blocks in Linux that are specific to macOS and vice versa so that the compiler does not fail to compile the code because certain standard libraries are missing or do not exist on certain platforms.
lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at AU $24.99/month. Cancel anytime