AWS Lambda Tutorial for Swift: Getting Started
Swift is now available in the world of serverless functions via AWS Lambda! Get started deploying your first on-demand serverless function with our AWS Lambda for Swift tutorial. By Ralph Kuepper.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
AWS Lambda Tutorial for Swift: Getting Started
20 mins
- Getting Started
- Creating your AWS Lambda Function
- Creating Your API Gateway
- Configuring Your API’s Routes
- Using Swift with Lambda and AWS
- Testing AWS Lambda Functions Locally
- Writing a Swift App for AWS Lambda
- Creating Your Function’s Model
- Writing Your Function’s Body
- Getting the Function Running on AWS
- Uploading Your Function to AWS Lambda
- Using Additional Services With AWS Lambda
- Where to Go From Here?
Writing a Swift App for AWS Lambda
Open the starter project in Xcode by clicking on Package.swift. Take note of the following two dependencies in this file:
// 1
.package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git",
.upToNextMajor(from:"0.1.0")),
// 2
.package(url: "https://github.com/swift-server/async-http-client.git",
from: "1.0.0")
The two dependencies are:
- The application needs the AWS Lambda function to have the official AWS Lambda framework available.
- The
AsyncHttpClient
to fetch a document from a server
Next, take a look at main.swift in Sources ▸ EURCurrencyRate. This file defines three global instances of classes you need in your functions:
//1
let jsonEncoder = JSONEncoder()
let jsonDecoder = JSONDecoder()
//2
let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)
defer {
try? httpClient.syncShutdown()
}
Here’s what’s happening in the code above:
-
JSONEncoder
andJSONDecoder
will read and write JSON content. - You need
HTTPClient
to pull the most current exchange rate from an external website. You’re creating a newEventLoop
by using the.createNew
static value for theeventLoopGroupProvider
parameter when initializing yourHTTPClient
.
EventLoop
by default and it doesn’t need to.At line 50, you see the function that AWS Lambda calls through the runtime framework:
Lambda.run {
(context,
request: APIGateway.V2.Request,
callback: @escaping (Result<APIGateway.V2.Response, Error>) -> Void) in
The framework defines input and output types you use in the function and return in the form of a callback function. The context of the request is defined as APIGateway.V2.Request
, which will tell you what kind of a request this is.
You also see a global convert
function, which will create the actual output of the AWS Lambda function on line 46.
You’ll get this function to react to the following two requests:
-
GET
:/convert
: Take the input in form of query parameters:?amount=1
-
POST
:/convert
: Take the input in the form of a JSON body.
Creating Your Function’s Model
Before you write the logic, create the following three types in separate files in EURCurrencyRate:
First, create RateInput.swift for a struct that resembles the input for the POST call:
import Foundation
struct RateInput: Codable {
// 1
let amount: Double
// 2
let currency: String?
}
The two inputs are:
- amount: This is the actual amount of money the function converts.
- currency: This is optional and will default to USD.
Now create a RateOutput.swift for a struct that represents the output of the conversion:
import Foundation
struct RateOutput: Codable {
// 1
let inputAmount: Double
// 2
let rate: Double
// 3
let inputCurrency: String
// 4
let outputAmount: Double
// 5
let outputCurrency: String
}
The variables in this struct represent:
-
inputAmount
: the original amount provided -
rate
: the applied conversion rate -
inputCurrency
: the input currency forinputAmount
-
outputAmount
: the converted amount, usinginputAmount
andrate
-
outputCurrency
: the desired currency
Finally, create a RateResponse.swift file containing the structure for the response from the API you use to determine the exchange rate:
import Foundation
struct RateResponse: Codable {
var rates: [String: Double]
var base: String
}
The API returns many rates; the logic will sort out which one to take.
Writing Your Function’s Body
Next you’ll write the business logic. Write the following code in the Lambda.run function in main.swift:
// 1
switch (request.context.http.path, request.context.http.method) {
// 2
case ("/convert", .GET):
// 3
let amount = Double(request.queryStringParameters?["amount"] ?? "0.0") ?? 0
let desiredCurrency = request.queryStringParameters?["currency"] ?? "USD"
// 4
convert(amount: amount, desiredCurrency: desiredCurrency, callback: callback)
// 5
case ("/convert", .POST):
// 6
if let data = request.body?.data(using: .utf8),
let input = try? jsonDecoder.decode(RateInput.self, from: data) {
convert(
amount: input.amount,
desiredCurrency: input.currency ?? "USD",
callback: callback)
} else {
// 7
callback(.success(APIGateway.V2.Response(statusCode: .badRequest)))
}
default:
// 8
callback(.success(APIGateway.V2.Response(statusCode: .notFound)))
}
Here’s what the code above does:
- Checks the context of this call. Remember AWS Lambda functions are often called internally and not always by a user and a browser.
- If it’s a
GET
call to/convert
, this case applies. - Since it’s a
GET
call, convert the query parameters into variables that you can pass to theconvert
function. - Call the convert function and pass the callback along.
- If it’s a
POST
call, this case applies. - Instead of parsing the variables from the URL, you decode the data from the request body into the
RateInput
struct. If this succeeds, call the convert function as well. - If, for some reason, like invalid JSON, the body doesn’t parse, return a
400: Bad Request
HTTP code. - If it’s a call to a different path or a different type of HTTP call, return a
404: Not Found
HTTP code.
Both functions then call the global convert
function to calculate the output and return it to the runtime.
Write the following code in the convert
function:
// 1
httpClient.get(url: "https://api.exchangeratesapi.io/latest").whenComplete {
result in
switch result {
case .failure(let error):
// 2
callback(.failure(error))
case .success(let response):
// 3
if let data = response.body, response.status == .ok {
let data = try? jsonDecoder.decode(RateResponse.self, from: data)
if let data = data {
// 4
for currency in data.rates.keys where currency == desiredCurrency {
// 5
let rate = Double(data.rates[currency] ?? 0)
// 6
let newAmount = rate * amount
// 7
let output = RateOutput(
inputAmount: amount,
rate: rate,
inputCurrency: "EUR",
outputAmount: newAmount,
outputCurrency: desiredCurrency)
// 8
if let data = try? jsonEncoder.encode(output) {
// 9
callback(.success(APIGateway.V2.Response(
statusCode: .ok,
multiValueHeaders: ["content-type": ["application/json"]],
body: String(decoding: data, as: Unicode.UTF8.self)
)))
}
}
}
callback(.success(APIGateway.V2.Response(statusCode: .badRequest)))
} else {
callback(
.success(APIGateway.V2.Response(statusCode: .internalServerError))
)
}
}
}
Here’s what’s happening in the code above:
- You call an API that returns conversion rates.
- If this fails, you return an error right away, as you can’t calculate anything.
- At this point, you at least have a response from the API, so now you parse it into the previously defined
RateResponse
struct. - You loop through the different rates to find the one specified in the input.
- Convert the value to a double.
- Calculate the new amount.
- Form the output as the
RateOutput
struct. - Convert this output to JSON.
- Call the callback function with the JSON string and a
200: OK
HTTP code.
This function is the heart of your program. Once you write this, you can start compiling! :]