Postgres-native durable workflow engine for Swift 6.3.
No separate coordination service. No Redis. No Cassandra. Just Swift workers and Postgres.
struct OrderWorkflow: Workflow {
typealias Input = OrderInput
typealias Output = ShipResult
mutating func run(
context: WorkflowContext<Self>,
input: OrderInput
) async throws -> ShipResult {
let charge = try await context.runActivity(
ChargeCardActivity.self,
input: .init(amount: input.amount)
)
return try await context.runActivity(
ShipOrderActivity.self,
input: .init(paymentID: charge.paymentID)
)
}
}If a worker crashes mid-workflow the next worker that picks it up resumes from the last checkpoint. No work is duplicated, no state is lost.
- Getting started — installation, first workflow, first worker
- Core concepts — execution model, checkpointing, determinism
- Activities — I/O, retries, heartbeats, timeouts
- Workflows — orchestration, fan-out, sleep, signals
- Scheduling — cron, intervals, catch-up
- Signals and events — external wakeup
- Retry strategies — backoff, non-retryable errors, deadlines
- Testing — integration test helpers
- Data pipelines — fan-out, multi-queue, crash recovery
# docker-compose.yml
services:
db:
image: postgres:18-alpine
environment:
POSTGRES_USER: strand
POSTGRES_PASSWORD: strand
POSTGRES_DB: strand_dev
ports:
- "5499:5432"docker compose up -d
psql "postgresql://strand:strand@localhost:5499/strand_dev" -f strand.sql.package(url: "https://github.com/thoven87/strand", from: "0.1.0"),import Strand
import ServiceLifecycle
let postgres = PostgresClient(configuration: .init(
host: "localhost", port: 5499,
username: "strand", password: "strand",
database: "strand_dev", tls: .disable
))
let client = StrandClient(postgres: postgres, queue: "default")
let worker = StrandWorker(
postgres: postgres,
options: WorkerOptions(queue: "default"),
workflows: [OrderWorkflow.self],
activities: [ChargeCardActivity(), ShipOrderActivity()]
)
let group = ServiceGroup(configuration: .init(
services: [.init(service: postgres), .init(service: worker)],
gracefulShutdownSignals: [.sigterm, .sigint]
))
// Start a workflow — returns a handle you can poll for the result:
let handle = try await client.startWorkflow(
OrderWorkflow.self,
input: OrderInput(amount: 99_00, orderID: "ord-1")
)
try await group.run()See Examples/ for complete runnable examples:
| Example | What it shows |
|---|---|
Greeting |
Minimal activity + workflow |
MultipleActivities |
Sequential and parallel activities |
Schedule |
Cron and interval scheduling |
ChildWorkflows |
Fan-out orchestration |
- Swift 6.3+
- PostgreSQL 15+
| Package | Version |
|---|---|
| PostgresNIO | 1.32.2 |
| Hummingbird | 2.22.0 |
| swift-service-lifecycle | 2.11.0 |
| swift-log | 1.12.0 |
| swift-metrics | 2.10.1 |
| swift-distributed-tracing | 1.4.1 |
| swift-collections | 1.0.0+ |
| swift-nio | 2.77+ |
Apache 2.0
