Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 31 additions & 8 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import PackageDescription
let package = Package(
name: "pkl-swift-examples",
platforms: [
.macOS(.v13),
.macOS(.v13)
],
dependencies: [
.package(url: "https://github.com/vapor/vapor.git", from: "4.89.0"),
Expand All @@ -38,13 +38,36 @@ let package = Package(
.product(name: "Vapor", package: "vapor"),
]
),
.testTarget(name: "AppTests", dependencies: [
"Gen",
.target(name: "App"),
.product(name: "XCTVapor", package: "vapor"),
.executableTarget(
name: "BuildTimeEval",
dependencies: [
"Gen",
.product(name: "Vapor", package: "vapor"),
.product(name: "PklSwift", package: "pkl-swift"),
],
resources: [
.embedInCode("config.msgpack")
]
),
.testTarget(
name: "AppTests",
dependencies: [
"Gen",
.target(name: "App"),
.product(name: "XCTVapor", package: "vapor"),

// Workaround for https://github.com/apple/swift-package-manager/issues/6940
.product(name: "Vapor", package: "vapor"),
]),
.testTarget(
name: "BuildTimeEvalTests",
dependencies: [
"Gen",
.target(name: "BuildTimeEval"),
.product(name: "XCTVapor", package: "vapor"),

// Workaround for https://github.com/apple/swift-package-manager/issues/6940
.product(name: "Vapor", package: "vapor"),
]),
// Workaround for https://github.com/apple/swift-package-manager/issues/6940
.product(name: "Vapor", package: "vapor"),
]),
]
)
23 changes: 19 additions & 4 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ This is a basic HTTP server that is configured via Pkl in the `pkl/` directory.

== Requirements

* Swift +5.9
* Pkl +0.25.0
* Swift +6.1
* Pkl +0.30.0;s

== Project structure

Expand All @@ -23,7 +23,10 @@ This is a basic HTTP server that is configured via Pkl in the `pkl/` directory.
| Generated Swift sources from Pkl

| `Sources/App/`
| Swift files
| Swift files for runtime evaluation example

| `Sources/BuildTimeEval/`
| Swift files for build-time evaluation example
|===

== Codegen
Expand All @@ -45,6 +48,18 @@ With that installed, generate Swift sources via:
pkl-gen-swift pkl/**.pkl -o Sources/Gen
----

== Build-time evaluation

The example in `Sources/BuildTimeEval/` shows how the https://pkl-lang.org/main/current/bindings-specification/binary-encoding.html[`pkl-binary` encoding] can be used to configure applications.
This mechanism separates evaluation of Pkl code from application runtime by encoding the configuration data as `pkl-binary`, storing it in a file, and loading it later during application startup.
This example works identically to the runtime evaluation example, but does not require the application to spawn the Pkl CLI as a subprocess.

The configuration module is rendered as `pkl-binary` by running:
[source,bash]
----
pkl eval pkl/dev/config.pkl -f pkl-binary -o pkl/dev/config.msgpack
----

== Running the server

To run the project call `swift run`.
To run the project call `swift run`.
Empty file.
1 change: 1 addition & 0 deletions Sources/BuildTimeEval/config.msgpack
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
��Config�:file:///Users/jbasch/src/pkl-swift-examples/pkl/Config.pkl���hostname�localhost��port����tcpNoDelayÓ�backlog̀��serverName�Pkl Swift Example Server
33 changes: 33 additions & 0 deletions Sources/BuildTimeEval/configure.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//

import Gen
import PklSwift
import Vapor

// configures your application
public func configure(_ app: Application) async throws {
let conf = try! PklDecoder.decode(Config.Module.self, from: PackageResources.config_msgpack)
// configure with Pkl
app.http.server.configuration.hostname = conf.hostname
app.http.server.configuration.port = conf.port
app.http.server.configuration.tcpNoDelay = conf.tcpNoDelay
app.http.server.configuration.backlog = conf.backlog
app.http.server.configuration.serverName = conf.serverName

// register routes
try routes(app)
}
37 changes: 37 additions & 0 deletions Sources/BuildTimeEval/entrypoint.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//

import Logging
import Vapor

@main
enum Entrypoint {
static func main() async throws {
var env = try Environment.detect()
try LoggingSystem.bootstrap(from: &env)

let app = Application(env)
defer { app.shutdown() }

do {
try await configure(app)
} catch {
app.logger.report(error: error)
throw error
}
try await app.execute()
}
}
27 changes: 27 additions & 0 deletions Sources/BuildTimeEval/routes.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//

import Vapor

func routes(_ app: Application) throws {
app.get { _ async in
"It works!"
}

app.get("hello") { _ async -> String in
"Hello, world!"
}
}
13 changes: 8 additions & 5 deletions Tests/AppTests/AppTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,21 @@
// limitations under the License.
//===----------------------------------------------------------------------===//

@testable import App
import XCTVapor

@testable import App

final class AppTests: XCTestCase {
func testHelloWorld() async throws {
let app = Application(.testing)
defer { app.shutdown() }
try await configure(app)

try app.test(.GET, "hello", afterResponse: { res in
XCTAssertEqual(res.status, .ok)
XCTAssertEqual(res.body.string, "Hello, world!")
})
try app.test(
.GET, "hello",
afterResponse: { res in
XCTAssertEqual(res.status, .ok)
XCTAssertEqual(res.body.string, "Hello, world!")
})
}
}
34 changes: 34 additions & 0 deletions Tests/BuildTimeEvalTests/BuildTimeEvalTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//

import XCTVapor

@testable import BuildTimeEval

final class BuildTimeEvalTests: XCTestCase {
func testHelloWorld() async throws {
let app = Application(.testing)
defer { app.shutdown() }
try await configure(app)

try app.test(
.GET, "hello",
afterResponse: { res in
XCTAssertEqual(res.status, .ok)
XCTAssertEqual(res.body.string, "Hello, world!")
})
}
}
2 changes: 2 additions & 0 deletions pkl/dev/config.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
// limitations under the License.
//===----------------------------------------------------------------------===//

// generate config.msgpack: pkl eval pkl/dev/config.pkl -f pkl-binary -o Sources/BuildTimeEval/config.msgpack

amends "../Config.pkl"

hostname = "localhost"
Expand Down