Pkl Swift Bindings

repository·main·Indexed 41 days ago

https://github.com/apple/pkl-swift

Swift bindings for the Pkl configuration language, providing type-safe embedding of Pkl into Swift applications via code generation and evaluation. Includes the @pkl.swift/gen.pkl tool for transforming Pkl models into Swift structs and protocols, as well as PklSwift.Evaluator for executing Pkl logic and resolving modules.

Tokens
4.6K
Snippets
22
Records
28
Agent score
46%

What's inside Pkl Swift

  1. Integrate Pkl with Swift

    main

    Pkl provides a rich integration with Swift that allows you to embed the Pkl runtime directly into your Swift programs and generate Swift code from Pkl source files. This enables type-safe configuration loading within your Swift applications.

    @main
    class MyApp {
        static func main() async throws {
            let config = try await MyConfig.loadFrom(source: .path("config.pkl"))
            print("I'm running on host \(config.host)")
        }
    }
  2. Choose between PklSwift.withEvaluator and EvaluatorManager

    main

    There are two ways to interact with PklSwift.Evaluator, which differ in how they manage the lifecycle of the underlying pkl CLI child process:

    1. PklSwift.withEvaluator(options:_:): Spawns a new child process for every evaluator instance. This is sufficient for most use cases.
    2. PklSwift.EvaluatorManager.withEvaluator(options:_:): Spawns a single child process for the entire lifetime of the EvaluatorManager. Use this if you need to maintain multiple evaluators with different settings or separate caches while sharing a single process.

    Evaluation is performed by communicating with the pkl CLI via message passing.

  3. How Pkl classes map to Swift structs and protocols

    main

    Pkl classes are transformed into a combination of Swift structs and protocols to handle inheritance and polymorphism.

    • Standard class: Generates a Swift struct.
    • open class: Generates a Swift protocol and a conforming struct (e.g., struct PersonImpl: Person).
    • abstract class: Generates a Swift protocol only.
    • Inheritance: If a class extends another, the resulting struct includes all properties from the parent class.

    Limitation: Classes with recursive property types will generate an invalid struct due to Swift limitations.

    | Pkl class | Swift protocol | Swift struct |
    |---|---|---|
    | `class Person` | <none> | `struct Person` |
    | `open class Person` | `protocol Person` | `struct PersonImpl: Person` |
    | `abstract class Person` | `protocol Person` | <none> |
    | `class Person extends Being` | <none> | `struct Person: Being` |
  4. Implement custom readers for Pkl resources

    main

    You can extend how Pkl resolves resources and modules by implementing the PklSwift.ResourceReader or PklSwift.ModuleReader protocols.

    To use a custom reader, you must set the scheme field. For example, if you want to resolve expressions like read("secret:FOO"), you would register a reader with the scheme "secret". When a resource matches that scheme, the reader's read() method is invoked to retrieve the content.

  5. Understand how optional keys are represented in Swift

    main

    Starting with version 0.6.0, the code generation for optional keys changed. A Mapping or Map containing an optional key is now represented using OptionalDictionaryKey<T> in Swift.

    Example Mapping:

    • Pkl: Mapping<String?, String>
    • Swift: [OptionalDictionaryKey<String>: String]

    This change was implemented to correctly handle null keys which were previously decoded incorrectly.

    // Example of the new representation
    let myMap: [OptionalDictionaryKey<String>: String] = [:]
  6. Use the new @pkl.swift/gen.pkl CLI tool for code generation

    main

    As of version 0.8.0, the standalone pkl-gen-swift binary has been replaced by a tool based on the pkl:Command interface: @pkl.swift/gen.pkl.

    Requirements:

    • This tool requires Pkl 0.31 or later.

    For detailed usage instructions, refer to the codegen section of the project's quickstart guide.

    # Note: The specific command syntax is located in the quickstart guide, but the tool name is:
    @pkl.swift/gen.pkl
  7. Install pkl-swift in a Swift Package

    main

    To use pkl-swift in your project, add it as a dependency to your Package.swift file. You must also ensure that the Pkl CLI is installed on your machine.

    let package = Package(
        dependencies: [
            .package(url: "https://github.com/apple/pkl-swift", from: "{version}")
        ],
        targets: [
            .executableTarget(
                name: "my-application",
                dependencies: [.product(name: "PklSwift", package: "pkl-swift")]
            ),
        ]
    )
  8. Resolve Swift name conflicts with @swift.Name

    main

    The code generator converts Pkl names to Swift names by stripping non-alphanumeric characters and capitalizing the preceding letter. This can cause name collisions (e.g., My_Application and MyApplication both becoming MyApplication).

    To resolve these conflicts, use the @swift.Name annotation in your Pkl file to explicitly define the resulting Swift name.

    import "package://pkg.pkl-lang.org/pkl-swift/pkl.swift@{version}#/swift.pkl"
    
    @swift.Name { value = "MyCoolApplication" }
    class My_Application
    
    class MyApplication
  9. Install formatting tools

    main

    This project uses swiftformat and hawkeye for code formatting.

    To install swiftformat, use Homebrew:

    brew install swiftformat

    To install hawkeye, you must first have the Rust toolchain installed, then use cargo:

    cargo install hawkeye
    brew install swiftformat
    cargo install hawkeye
  10. Get started with pkl-swift

    main

    To begin using Pkl configuration within your Swift application, follow the official quick start guide. This library provides Swift bindings for the Pkl language, enabling you to embed Pkl configurations and use code generation to achieve full type safety in your Swift code.

    https://pkl-lang.org/swift/current/quickstart.html
  11. Convert Pkl unions to Swift enums using typealiases

    main

    To represent a Pkl union as a Swift enum, you must assign the union to a typealias. Each alternative in the union becomes an enum member. Other typealiases that are not unions are generated as standard Swift typealiases.

    typealias City = "San Francisco"|"Cupertino"|"London"

    Resulting Swift code:

    enum City: String, CaseIterable, Decodable, Hashable {
        case sanFrancisco = "San Francisco"
        case cupertino = "Cupertino"
        case london = "London"
    }