Serverless функции на Swift: AWS Lambda от А до Я

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение в Serverless на Swift с AWS Lambda

Serverless-архитектура становится стандартом современной облачной разработки. Благодаря AWS Lambda разработчики могут запускать код без управления серверами, платя только за фактическое время выполнения. До недавнего времени Swift оставался в стороне от serverless-мира, но с появлением официальной поддержки Swift на AWS Lambda ситуация кардинально изменилась.

В этой статье мы разберем полный цикл работы с serverless функциями на Swift: от настройки окружения до деплоя в production. Вы узнаете, как использовать свой опыт iOS/macOS разработки для создания эффективных облачных решений.

Почему Swift для Serverless?

Swift — это не только язык для мобильных приложений. Его сильные стороны делают его отличным выбором для serverless:

  • Безопасность типов — компилятор отлавливает ошибки на этапе сборки
  • Высокая производительность — нативный код работает быстрее интерпретируемых языков
  • Современный синтаксис — код легко читать и поддерживать
  • Общая кодовая база — можно переиспользовать модели и логику из iOS/macOS проектов

Для запуска Swift на AWS Lambda используется официальный образ Amazon Linux 2 с предустановленным Swift 5.10. Это гарантирует полную совместимость и минимальный холодный старт.

Настройка окружения и создание первой функции

Установка необходимых инструментов

Прежде чем начать, убедитесь, что у вас установлены:

  • Swift 5.10 или новее
  • Docker (для сборки в Linux-окружении)
  • AWS CLI с настроенными credentials
  • Homebrew (для macOS)
# Установка Swift через Homebrew
brew install swift

# Проверка версии
swift --version

# Установка AWS CLI
brew install awscli

# Настройка AWS
aws configure

Создание проекта Swift Package

Создадим новый исполняемый пакет Swift:

mkdir MyLambdaFunction
cd MyLambdaFunction
swift package init --type executable

Теперь обновим Package.swift для работы с AWS Lambda Runtime:

// swift-tools-version:5.10
import PackageDescription

let package = Package(
    name: "MyLambdaFunction",
    platforms: [.macOS(.v13)],
    dependencies: [
        .package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", from: "1.0.0")
    ],
    targets: [
        .executableTarget(
            name: "MyLambdaFunction",
            dependencies: [
                .product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime")
            ]),
        .testTarget(
            name: "MyLambdaFunctionTests",
            dependencies: ["MyLambdaFunction"]),
    ]
)

Код функции-обработчика

Заменим содержимое Sources/MyLambdaFunction/main.swift:

import AWSLambdaRuntime

// Определяем структуру запроса
struct Request: Codable {
    let name: String
    let age: Int
}

// Определяем структуру ответа
struct Response: Codable {
    let message: String
    let isAdult: Bool
}

// Основной обработчик
@main
struct MyHandler: SimpleLambdaHandler {
    typealias Event = Request
    typealias Output = Response

    func handle(_ event: Request, context: LambdaContext) async throws -> Response {
        let isAdult = event.age >= 18
        return Response(
            message: "Привет, \(event.name)!",
            isAdult: isAdult
        )
    }
}

Сборка и деплой на AWS Lambda

Сборка для Linux

Поскольку Swift на macOS собирает бинарники для macOS, нам нужно использовать Docker:

# Создаем Dockerfile
cat << EOF > Dockerfile
FROM swift:5.10-amazonlinux2 AS build
WORKDIR /app
COPY . .
RUN swift build -c release --static-swift-stdlib

FROM amazonlinux:2
RUN yum install -y libcurl-openssl1.0
COPY --from=build /app/.build/release/MyLambdaFunction /main
ENTRYPOINT ["/main"]
EOF

# Собираем образ
docker build -t my-lambda-function .

# Извлекаем бинарник
docker create --name extract my-lambda-function
docker cp extract:/main ./bootstrap
docker rm extract

# Упаковываем
zip function.zip bootstrap

Деплой через AWS CLI

# Создаем Lambda функцию
aws lambda create-function \
    --function-name MySwiftFunction \
    --runtime provided.al2 \
    --role arn:aws:iam::YOUR_ACCOUNT_ID:role/lambda-execution-role \
    --handler main \
    --zip-file fileb://function.zip

# Тестируем
aws lambda invoke \
    --function-name MySwiftFunction \
    --payload '{"name": "Иван", "age": 25}' \
    response.json

# Смотрим результат
cat response.json
# {"message":"Привет, Иван!","isAdult":true}

Продвинутые техники: работа с API Gateway и базой данных

Интеграция с API Gateway

Для создания REST API обернем нашу функцию в обработчик HTTP-запросов:

import AWSLambdaRuntime
import AWSLambdaEvents

@main
struct APIGatewayHandler: SimpleLambdaHandler {
    typealias Event = APIGatewayV2Request
    typealias Output = APIGatewayV2Response

    func handle(_ event: APIGatewayV2Request, context: LambdaContext) async throws -> APIGatewayV2Response {
        // Парсим тело запроса
        guard let body = event.body,
              let data = body.data(using: .utf8),
              let request = try? JSONDecoder().decode(Request.self, from: data) else {
            return APIGatewayV2Response(statusCode: .badRequest, body: "Invalid request")
        }

        // Формируем ответ
        let response = Response(
            message: "Hello, \(request.name)!",
            isAdult: request.age >= 18
        )

        let jsonData = try JSONEncoder().encode(response)
        let jsonString = String(data: jsonData, encoding: .utf8) ?? "{}"

        return APIGatewayV2Response(statusCode: .ok, body: jsonString)
    }
}

Работа с DynamoDB

Для сохранения данных используем AWS SDK для Swift:

import AWSLambdaRuntime
import AWSDynamoDB

struct User: Codable {
    let id: String
    let name: String
    let email: String
}

@main
struct DynamoDBHandler: SimpleLambdaHandler {
    typealias Event = Request
    typealias Output = Response

    let dynamoDB = DynamoDBClient()

    func handle(_ event: Request, context: LambdaContext) async throws -> Response {
        let user = User(id: UUID().uuidString, name: event.name, email: event.email)

        // Сохраняем в DynamoDB
        let input = PutItem                

Блоги

Рекомендаций по книгам