Setting up a consistent, cross-platform Swift development environment can be tricky, especially when you need to interact with AWS services. This guide will walk you through creating a powerful, containerized setup using Visual Studio Code, Dev Containers, and LocalStack.
You'll get a fully functional Swift environment that runs inside Docker, complete with debugging, code completion, and a local AWS cloud stack for all your testing needs. Best of all? It works identically on macOS, Linux, and Windows.
LocalStack is a fully functional local AWS cloud stack that allows you to test your cloud applications locally without connecting to the real AWS cloud. It provides a set of APIs that mimic the behavior of AWS services, making it easy to develop and test your applications in a safe and controlled environment.
Before we start, make sure you have these tools installed on your system:
- Docker
- Visual Studio Code
- Visual Studio Code Dev Containers Extension
- Docker Compose V2
- It's also helpful to review the official guide on configuring VS Code for Swift and install Swift extension.
Let's build our environment from the ground up.
Open your terminal, create a new directory for your project, and navigate into it.
mkdir SwiftInDevContainer
cd SwiftInDevContainerLaunch VS Code and open the SwiftInDevContainer folder you just created.
Open the VS Code integrated terminal and use the Swift Package Manager to create a new executable project.
swift package init --type executable --name SwiftInDevContainerFor Swift code completion and language features to work, we need to generate a config file.
- Open the command palette: Cmd+Shift+P (macOS) or Ctrl+Shift+P (Windows/Linux).
- Type and select "Swift: Generate SourceKit-LSP Configuration...".
This will create a sourcekit-lsp/config.json file. It should look like this:
{
"$schema": "https://raw.githubusercontent.com/swiftlang/sourcekit-lsp/refs/heads/release/6.2/config.schema.json"
}Let's make sure you can build and run locally first. Try running the debugger. The Swift extension should automatically generate a .vscode/launch.json file for you.
Note: This initial launch.json will contain paths specific to your local machine (like /Users/yourname/Documents...). This is expected! We'll replace this file once we're inside the container.
Now, let's tell VS Code how to build our container.
- Open the command palette (Cmd+Shift+P / Ctrl+Shift+P).
- Type and select "Dev Containers: Add Dev Container Configuration Files...".
- Choose "Add configuration to workspace".
- Choose "Docker outside of Docker Compose".
- Select "latest".
- Select the default options
- When prompted to add features, select "AWS CLI".
- You can also select
.github/dependabot.ymlif you plan to use it.
This will create a .devcontainer folder with a devcontainer.json and docker-compose.yml file.
We need to customize this file to install the Swift extension inside the container and configure the LLDB debugger.
Open .devcontainer/devcontainer.json and add the "customizations" block shown below. This will automatically install the Swift extension and tell the debugger where to find the correct library inside the container.
Next, open .devcontainer/docker-compose.yml. We need to:
- Add the LocalStack service.
- Make our
appservice depend on LocalStack. - Add environment variables so our Swift app can find LocalStack.
- Enable
ptracefor the debugger to work.
Your final docker-compose.yml should look like this:
version: '3'
services:
app:
build:
context: .
dockerfile: Dockerfile
volumes:
# Forwards the local Docker socket to the container.
- /var/run/docker.sock:/var/run/docker-host.sock
# Update this to wherever you want VS Code to mount the folder of your project
- ../..:/workspaces:cached
# Overrides default command so things don't shut down after the process ends.
entrypoint: /usr/local/share/docker-init.sh
# Add dependency on localstack
depends_on:
- localstack
# Add environment variables for AWS SDK
environment:
- LOCALSTACK_ENDPOINT=http://localstack:4566
- AWS_ACCESS_KEY_ID=test
- AWS_SECRET_ACCESS_KEY=test
- AWS_REGION=us-east-1
command: sleep infinity
# Uncomment the next four lines if you will use a ptrace-based debuggers like C++, Go, and Rust.
cap_add:
- SYS_PTRACE
security_opt:
- seccomp:unconfined
# Use "forwardPorts" in **devcontainer.json** to forward an app port locally.
# (Adding the "ports" property to this file will not forward from a Codespace.)
# Add the localstack service
localstack:
image: localstack/localstackIn the same .devcontainer folder, change the Dockerfile content. This file will define our main app container.
We'll use an official Swift image and add make, which is useful for utility scripts.
FROM swift:6.2.0
# Add make installation
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install --no-install-recommends makeNow for the magic.
- Open the command palette (Cmd+Shift+P / Ctrl+Shift+P).
- Type and select "Dev Containers: Rebuild and Reopen in Container".
VS Code will now build your Dockerfile, start the docker-compose services (your Swift app and LocalStack), and reload the window, connecting you inside the app container. You'll know it worked when the bottom-left corner of VS Code shows "Dev Container: ...".
Remember that old launch.json from Step 5? It's pointing to your local machine's paths, which don't exist inside the container. Let's fix it.
- You can delete the old
.vscode/launch.jsonfile if you want, or just let this next step overwrite it. - Select the
Package.swiftfile in the Explorer to make it the active file. - Open the command palette (Cmd+Shift+P / Ctrl+Shift+P).
- Type and select "Swift: Generate Launch Configuration".
This will create a new launch.json with the correct container paths (e.g., /workspaces/SwiftInDevContainer/.build/debug/SwiftInDevContainer).
Now, you can start Debugging. The project will build inside the container, and the "Hello, world!" output will appear in your debug console.
Let's prove our container can talk to the LocalStack container. We'll add Soto, the Swift SDK for AWS, to query DynamoDB.
First, update your Package.swift to include soto-dynamodb:
// swift-tools-version: 6.2
import PackageDescription
let package = Package(
name: "SwiftInDevContainer",
dependencies: [
.package(url: "https://github.com/soto-project/soto.git", from: "7.0.0"),
],
targets: [
.executableTarget(
name: "SwiftInDevContainer",
dependencies: [
.product(name: "SotoDynamoDB", package: "soto"),
]
),
]
)Next, update Sources/SwiftInDevContainer/SwiftInDevContainer.swift to list DynamoDB tables:
import SotoDynamoDB
@main
struct SwiftInDevContainer {
static func main() async throws {
// Use the environment variables we set in docker-compose.yml
let awsClient = AWSClient()
// Point the client at our LocalStack container
let dynamoDB = DynamoDB(client: awsClient, endpoint: "http://localstack:4566")
print("List tables")
let result = try await dynamoDB.listTables()
print("result: \(result)")
try await awsClient.shutdown()
}
}Run the debugger again. VS Code will resolve the new package and run the code. You should see this output, as we haven't created any tables yet:
List tables
result: ListTablesOutput(lastEvaluatedTableName: nil, tableNames: Optional([]))
Let's create a table using the AWS CLI (which we installed in our container) and make.
Create a Makefile in the root of your project:
localstack_create_dynamo_db_table:
aws --endpoint-url=http://localstack:4566 dynamodb create-table \
--table-name Breeze \
--attribute-definitions AttributeName=itemKey,AttributeType=S \
--key-schema AttributeName=itemKey,KeyType=HASH \
--billing-mode PAY_PER_REQUEST \
--region us-east-1Now, in the VS Code terminal (which is inside the container), run the command:
make localstack_create_dynamo_db_tableThis tells the AWS CLI to connect to our localstack service and create a table named "Breeze".
Run your Swift app one more time (F5). This time, the output will show your new table!
List tables
result: ListTablesOutput(lastEvaluatedTableName: nil, tableNames: Optional(["Breeze"]))
Congratulations! You now have a fully containerized, cross-platform Swift development environment. You can build, debug, and test your Swift applications that interact with AWS services, all without needing a real AWS account or installing Swift on your host machine.
If you're interested in taking this further and building Swift serverless applications with AWS Lambda and DynamoDB, check out the Breeze project.
Happy coding!
Don't forget to check out my blog: www.andrea-scuderi.com
