Provide examples and benchmarks for a Swift package

Is there a preferred approach for providing examples and benchmarks for a Swift package? I have a GitHub project for a Swift package that looks like the following:

mypackage/
├── Sources/
│   └── MyPkg/
│       └── Funcs.swift
├── Tests/
│   └── TestFuncs.swift
├── README.md
└── Package.swift

I would like to provide some examples that demonstrate using the package. I also want to provide some files that can be run as benchmarks. The most straightforward approach would be to add directories in the project for examples and benchmarks like the following:

mypackage/
├── Sources/
│   └── MyPkg/
│       └── Funcs.swift
├── Tests/
│   └── TestFuncs.swift
├── Benchmarks/
│   └── addition.swift
├── Examples/
│   ├── adder.swift
│   └── subtracter.swift
├── README.md
└── Package.swift

Is this a reasonable approach or should I make a separate repository for examples and benchmarks?

1 Like

If your examples, or benchmarks, don't need additional dependencies then that's a lovely way to arrange things - dropping them into their own modules, possibly even with their own targets.

When there's dependencies you don't want to flow down to consumers of your package (which happens in a number of my own bits), I generally make a subdirectory and put benchmarks (using package-benchmark in my case) into it as a separate swift project, with a local package link to the library in order to run the benchmarks.

The same with generally with any of examples, either as a small executable client, or even a full-out xcode project if there's UI involved. The downside is that if you want to verify your example (or benchmark) runs through CI, your local CI setup gets a bit more complicated.

In one case I went to pushing the example sub-project into it's own repository, but I waited until my library stabilized and it was reflecting the latest released version of the library, otherwise the iteration as you're updating the library and the example at the same time became quite the tedious chore. YMMV.

3 Likes

These are good points. Especially about how tedious it would be to keep separate repositories in sync with each other. I don't want to deal with changes in the package repository breaking something in a separate examples repository.

How would I run a file in the Examples directory? Can I run it from within Xcode or do I have to run it from the terminal? If I compile and run it from the terminal, will the package automatically get imported?

When I have a separate example Package in a subdirectory, I typically use the terminal - switch to that directory, and then either run on the command line or open the Package.swift in that subdirectory to fiddle with it in Xcode.

Xcode will have a bit of a fit if you try and have both projects open at once, if the subdirectory one has a "local reference" to the parent directory. So if you want to fiddle with both, don't open the main Package.swift, only open the one in the subdirectory.

Can you point me to some Swift packages on GitHub that also provide examples of the package?

For benchmarks, one that I recently worked on is swift-noise - the library itself is in the main package/root of that repository, and benchmarks in the subdirectory ExternalBenchmarks. In this package, there was already an executable CLI that was part of the project, so its examples are inline - not broken out.

My other case for an external example used a separate repository entirely, as it's a full SwiftUI app that uses a library, so it's not the subdirectory setup at all. (MeetingNotes as an example app using Automerge-Swift)

2 Likes

It looks like these examples and benchmarks are entire Xcode projects or packages themselves. Is there a way to provide examples/benchmarks as just a Swift file? Consider a directory of examples like shown below where each example is just a Swift file.

mypackage/
├── Sources/
│   └── MyPkg/
│       └── Funcs.swift
├── Tests/
│   └── TestFuncs.swift
├── Benchmarks/
│   └── addition.swift
├── Examples/
│   ├── example1.swift
│   └── example2.swift
├── README.md
└── Package.swift

The content of an example file might be something like:

import MyPkg

let a = adder(1, 2)
print("a is", a)

You would run an example from the command line using:

cd Examples/
swift example1.swift

This approach is common for Python packages where examples and benchmarks are just scripts that can be run from the terminal where python example1.py would run that example. I would like to use a similar approach for examples/benchmarks for a Swift package.

The short answer is yes, but it turns out to not be entirely useful, at least in my experience. My examples have all been packages because when you're invoking swift example1.swift in a directory, it doesn't have any means of getting and using dependencies. That all comes from the package setup. Some of the "using swift to write scripts work" has been to work around that constraint (for example, check out GitHub - mxcl/swift-sh: Easily script with third-party Swift dependencies.)

So I just go ahead and establlsh a Package.swift, add a dependency to reference the package I want locally, and then I can swift example1.swift and have it import the pieces from the library that I'm trying to show off.

This may have evolved since I last dug into it, but if so I'm not aware of it. I'm not 100% on if you run swift example/myexample.swift or from a root project directory if that makes the package defined in that directory available or not. There the --repl option that was added a few years back that might make this a bit more feasible as well: Swift.org - REPL Support for Swift Packages

this sounds like an excellent use case for SwiftPM Snippets, which you could run with swift run MyExample!

2 Likes

What are SwiftPM snippets? Is this a proposed feature or existing feature?

What are SwiftPM snippets?

I’m sure about the state of this feature, but here’s the kick-off pitch: Swift Snippets.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

It is an existing and operational feature - partially implemented in earlier versions of Swift, and fully implemented in Swift 5.10 - but not supported in Xcode (Xcode 15.x and the latest 16 beta3 don't support referencing Snippets with documentation when built through Xcode. Likewise, no significant documentation for it, and the marquee example effort that shows its usage has historically been the swift-markdown project.

In terms of using it within Documentation, you'll need to invoke documentation builds from the command line (outside of Xcode entirely) to see the results.

The kick-off pitch that @eskimo referenced covers most of it, and I think @taylorswift has recently been trying to use it more aggressively in some of their projects. I've had experiments to the same, but since I do most of my code editing in Xcode, I've personally been holding off adopting it any more aggressively until there's Xcode support and clear documentation. That said, my work-arounds for tracking code snippets for example docs and making sure they're aligned with the latest library (that they still compile, etc) are ugly hacks at best.

1 Like