/swift-template-openfaas

Allows the usage of Swift within OpenFaaS containers, offering support for the Swift Package Manager (SPM) to be used.

Primary LanguageSwiftMIT LicenseMIT

Swift Template for OpenFaaS

Description

Allows the usage of Swift 4.2.4 within OpenFaaS containers, offering support for the Swift Package Manager (SPM) to be used. This greatly widens what is possible within your Swift function while keeping your code at a minimum. On top of the ability to use packages, multiple source files may be used to keep your function organized.

This template is based heavily on the work already in place by Keiran Smith for using Swift within the OpenFaaS system. A fair portion of the code and basis for the Docker images were taken directly from affix/openfaas-templates-affix.

Usage

Usage of the Swift language template assumes that faas-cli is already installed on the development machine, for cli installation help please read the documentation.

From the directory you want to house the function, enter the following commands in your terminal.

faas-cli template pull https://github.com/lorenalexm/swift-template-openfaas
faas-cli new {projectname} --lang swift

At this point you will have a directory which contains the {projectname}.yml file, along with the template directory OpenFaaS needs for knowing how to build the image, and finally a {projectname} directory which contains the Handler.swift and Package.swift files where you will flesh out your function.

The handler.swift file houses by default a single function: process(with args: String), returning a String, wrapped in the Handler class. The argument passed into process(with args: String) might be more than a basic String object, and could even be JSON presented to your function as a string; allowing you to use the JSONDecoder together with a Codable struct.

Once your function is fleshed out and ready for use, and keeping with the simplicity of OpenFaaS, simply issue the following command from the base directory of your function. This will build the image for the function, push it to your container registry, and send it off to your OpenFaaS installation.

faas-cli up -f {projectname}.yml

Examples

Barebones

This function example does nothing more than return a success string with each request.

// Handler.swift
class Handler {
	func process(with args: String) -> String {
	    return "Success"
	}
}
Codable example

A slightly more in-depth example, making use of the JSONDecoder class and Codable. A JSON request is posted to the function endpoint, the function takes this in as a string, decodes it to the person struct, and returns the name and age as a string.

// Handler.swift
import Foundation

struct Person: Codable {
	var name: String
	var age: Int
}

class Handler {
	func process(with args: String) -> String {
		guard let json = args.data(using: .utf8) else {
			return "Unable to encode string to data"
		}
		let decoder = JSONDecoder()
		if let person = try? decoder.decode(Person.self, from: json) {
			return "Hello \(person.name), you are \(person.age) years old."
		} else {
			return "Unable to decode string"
		}
	}
}
Package example

A final example, demonstrating use of the Package.swift file, and the decoding and encoding of a struct from a JSON request. A single value will be passed into the function, and two will be returned; the original and the hashed value. Fetching of the required dependencies is handled when your function is built and sent to the OpenFaaS service, negating the need to pull them manually.

// Package.swift
import PackageDescription

let package = Package(
    name: "Handler",
    dependencies: [
        .package(url: "https://github.com/alexaubry/ServerCrypto.git", from: "1.0.0")
    ],
    targets: [
        .target(
            name: "Handler",
            dependencies: ["Hash"])
    ]
)
// Handler.swift
import Foundation
import Hash

struct Message: Codable {
	var original: String
	var hashed: String?
}

class Handler {
	func process(with args: String) -> String {
		guard let json = args.data(using: .utf8) else {
			return "Unable to extract data from string"
		}
		let decoder = JSONDecoder()
		if var message = try? decoder.decode(Message.self, from: json) {
			let hasher = Hasher.md5
			if let hashed = try? hasher.makeHash(for: message.original.data(using: .utf8)!) {
				message.hashed = hashed.hexString
				let encoder = JSONEncoder()
				if let data = try? encoder.encode(message) {
					return String(data: data, encoding: .utf8)!
				} else {
					return "Unable to encode struct"
				}
			} else {
				return "Unable to generate hash"
			}
		} else {
			return "Unable to decode string"
		}
	}
}

License

This project is licensed under the MIT license, see LICENSE file for further details.