Lambda SPM Resources
Posted Tuesday, February 6, 2024.
There is an additional step you need to do when packaging an SPM based Lambda that has resources.
Project Setup
File Structure
.
├── Sources
│   ├── Lambda
│   │   └── Handler.swift
│   └── Resources
│       └── info.txt
├── Package.swift
└── Package.resolved
 
Package.swift
// swift-tools-version: 5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
    name: "Lambda",
    platforms: [
        .macOS(.v13)
    ],
    dependencies: [
        .package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", from: "1.0.0-alpha.2")
    ],
    targets: [
        .executableTarget(
            name: "Lambda",
            dependencies: [
                .product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime")
            ],
            resources: [
                .process("Resources")
            ]
        )
    ]
)
 
Handler Definition
Imagine we had a handler that would look for a resource decided by the request parameters:
import AWSLambdaRuntime
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
struct Request: Codable {
    let resource: String
    let extension: String
}
struct Response: Codable {
    let output: String
}
@main
struct Handler: SimpleLambdaHandler {
    func handle(_ request: Request, context: LambdaContext) async throws -> Response {
        guard let url = Bundle.module.url(forResource: request.resource, withExtension: request.extension) else {
            throw Error.missingResource
        }
        let data = try Data(contentsOf: url)
        let string = String(decoding: data, as: UTF8.self)
        return .init(output: string)
    }
    enum Error: Swift.Error {
        case missingResource
    }
}
 
Without doing any extra steps, Bundle.module.url(forResource:withExtension:) would return nil when running on the Lambda because the resources are not bundled properly (even if the resource was "supposed" to exist).
Steps to Mitigate
# use package plugin to bundle lambda
swift package --disable-sandbox archive --output-path .
# remove generated lambda zip
rm ./Lambda/Lambda.zip
# Copy Resources from build directory (PackageName_TargetName.resources)
cp -r .build/release/Lambda_Lambda.resources ./Lambda/Lambda_Lambda.resources
# zip new lambda with resources included
zip -r Lambda.zip Lambda_Lambda.resources bootstrap Lambda
 
Upload
Upload new Lambda zip to AWS
Tagged With: