Swift Summer of Code 2022 Summary

Google Summer of Code (also known as GSoC) is a long-running mentorship program focused on introducing contributors to the world of open source development. This year marks the fifth time the Swift project has participated in GSoC.

During the 2022 edition of the program, we were lucky to work with five great contributors, all of which completed their assigned projects successfully. We would like to thank all the contributors – Amritpan, Felix, Fredrik, Kth and Sofía – for the time and passion they poured into their projects:

To shine a light on their exceptional work and to inspire future participants, let’s take a closer look at their accomplishments.

Bootstrapping SwiftSyntaxBuilder

Author: Fredrik Wieczerkowski
Mentor: Alex Hoppen

The SwiftSyntax library, which lets users represent, parse, and generate Swift source code using Swift, received major updates. The result builder-based SwiftSyntaxBuilder Domain-Specific Language (DSL) was improved and inconveniences in the API surface were fixed and thoroughly tested.

During this process, the templates that generated part of SwiftSyntaxBuilder’s sources were ported from gyb to type-safe Swift code based on SwiftSyntaxBuilder. In other words, the library now uses itself to generate its own code!

The result of this bootstrapping process is a more robust and ergonomic API for generating Swift code.

Here’s an example of how Swift’s expressiveness allows for builder closures which closely resemble the generated source code. For the following snippet of Swift code:

struct Point {
  let x: Int
  let y: Int
}

The corresponding SwiftSyntaxBuilder DSL would be:

StructDecl(identifier: "Point") {
  VariableDecl(.let, name: "x", type: "Int")
  VariableDecl(.let, name: "y", type: "Int")
}

For more information, check out the project’s writeup.

Improving the debug output of the type inference algorithm

Author: Amritpan Kaur
Mentor: Pavel Yaskevich

Swift’s type inference algorithm is at the heart of the Swift developer experience. It is the algorithm that allows us to write source code without always providing explicit type information to the compiler.

The algorithm is implemented via a constraint-based type checker that gathers available type context from the source code and attempts to solve for a concrete type for those parts of the source code that are missing type information, resulting in a valid, fully-typed Swift expression.

The type inference algorithm produces an output for debugging that is especially helpful when working with invalid expressions. However, this output has been difficult to understand and unwieldy:

$T0 [lvalue allowed] [noescape allowed] delayed bindings={} @ locator@0x1258ee200 [OverloadedDeclRef@/…]
$T1 [noescape allowed] delayed literal=3 bindings={} @ locator@0x1258f0ac0 [IntegerLiteral@/…]
$T2 [noescape allowed] delayed literal=3 bindings={} @ locator@0x1258f0b78 [StringLiteral@/…]
$T3 [noescape allowed] delayed bindings={} @ locator@0x1258f0c40 [Binary@/… -> function result]

This output has repetitive elements, seems disjointed, and does not show essential type and process details.

Over the last few months, debug output was reworked and reformatted the output to improve its readability. To make it easier to follow how an expression or subexpression is type checked, the output now closely tracks constraint solver steps by showing constraint simplification and solver scope changes. The output also explicitly states important type information in context to show how the constraint solver’s path changes type variable bindings and relationships. Additionally, a redesigned layout groups together type properties, simplification process, and nested constraint solver scopes for a more visually friendly format:

$T0 [allows bindings to: lvalue, noescape] [attributes: delayed] [with possible bindings: <empty>]) @ locator@0x13ca3e400 [OverloadedDeclRef@/…]
$T1 [allows bindings to: noescape] ($T1 [attributes: delayed, [literal: integer]] [with possible bindings: (default type of literal) Int]) @ locator@0x13ca3f398 [IntegerLiteral@/…]
$T2 [allows bindings to: noescape] [attributes: delayed, [literal: string]] [with possible bindings: (default type of literal) String]) @ locator@0x13ca42e68 [StringLiteral@/…]
$T3 [allows bindings to: noescape] [attributes: delayed] [with possible bindings: <empty>]) @ locator@0x13ca42f30 [Binary@/… -> function result]

Check out Amritpan’s forum post for more information.

Interactive mode for ArgumentParser

Author: Kth
Mentor: Nate Cook

swift-argument-parser provides a fast and easy way to create high-quality, user-friendly command-line tools in Swift.

The upcoming interactive mode can prompt for missing inputs to help guide users through unfamiliar command line tools. The interactive mode continues ArgumentParser’s approach of providing a lightweight coding experience, building on the metadata tool authors already provide.

$ roll --help
USAGE: roll --times <n> --sides <m> [--verbose]

OPTIONS:
  --times <n>             Rolls the dice <n> times.
  --sides <m>             Rolls an <m>-sided dice.
  -v, --verbose           Show all roll results.
  -h, --help              Show help information.

$ roll --verbose --times 3
? Please enter 'sides': 6
Roll 1: 1
Roll 2: 6
Roll 3: 3
Total: 10

Interactive mode will be included in a future ArgumentParser release, but you can try it out now on the feature/interactive branch.

Quick navigation in Swift-DocC websites

Author: Sofía Rodríguez
Mentors: Marina Aísa, Franklin Schrans, Beatriz Magalhaes

This feature creates a fast and accessible way to navigate and discover symbols in Swift-DocC documentation websites, similar to Open Quickly in Xcode.

Swift-DocC quick navigation

Key features

Check out Sofía’s forum post for more information.

Kafka client package for Swift

Author: Felix Schlegel
Mentor: Franz Busch

SwiftKafka is a new Swift package that provides a convenient way to communicate with Apache Kafka servers. During the package development, the main goal was to create an API that leverages Swift’s new concurrency features. Under the hood, this package uses the librdkafka C library, wrapping the unsafe and blocking APIs into safer, more ergonomic Swift APIs.

Here are examples showing how to use the new package to produce and consume messages from a Kafka server:

Producer API

The sendAsync(_:) method of KafkaProducer returns a message-id that can be used to identify the corresponding acknowledgement. Acknowledgements are received through the acknowledgements AsyncSequence. Each acknowledgement indicates that producing a message was either successful or returns an error.

let producer = try await KafkaProducer(
    config: .init(),
    logger: logger
)
let messageID = try await producer.sendAsync(
     KafkaProducerMessage(topic: "topic-name", value: "Hello, World!")
)
for await acknowledgement in producer.acknowledgements {
    /// ...
}

Consumer API

After initializing the KafkaConsumer with a topic-partition pair, messages can be consumed using the messages AsyncSequence.

let consumer = try KafkaConsumer(
    topic: "topic-name",
    partition: KafkaPartition(rawValue: 0),
    config: .init(),
    logger: logger
)
for await messageResult in consumer.messages {
    // ...
}

Wrap up

For more information, as well as previous year’s projects, check out: