Swift Snippets

Swift Snippets

Hello everyone,

Continuing the discussion started in Command Line UX Enhancements for swift, I'd like to introduce some experimental work for a new Swift Package Manager feature for snippets.

We all know that learning by example is great, especially for code. I wanted to create the smallest, simplest method of providing example code for Swift packages, and I've just landed some work in progress in the Swift Package Manager.

First and foremost a teaching tool, snippets are a small, focused, single file of example code that teaches a specific concept or API. Each snippet is a piece of the puzzle for documenting and showing best practices for your packages. They can also stand alone as a series of exercises, examples, or code recipes.

Each snippet file implies an executable target in the package that builds alongside the other targets in a package, ensuring that your examples always build. They can automatically import any library that a package defines, along with those normally available in an SDK, or whatever is the equivalent for your platform.

You can think of this work on snippets as both a new file format for sample code, and an initial implementation of a UX for interacting with those snippets (via SwiftPM.) It is easy to imagine other editors, IDEs, education apps, or documentation tools interacting with snippet files to teach in new and exciting ways.

Writing and organizing snippets in a package

Here's an example snippet file:

//! Does a foo.

import MyModule

func foo() {}

// MARK: Hide

print(...)

// MARK: Show

func bar() {
    foo()
}

The //! comment is presented as a brief explanation for the snippet. This comment does not show inline in the source but at the top or in a listing for searching and browsing. Feel free to use regular comments if you need to explain the flow of a snippet.

The MARK: Hide comment tells the snippet system that the line and subsequent lines should be hidden but kept for building and running. A MARK: Show line will toggle that and lines will start showing again. This is how you separate presentation code from demo code, or glue code if you like.

The above snippet would present via SwiftPM like so:

Does a foo.

import MyModule

func foo() {}

func bar() {
    foo()
}

To start adding snippets to a package, create a Snippets directory alongside the familiar Sources and Tests directory and start dropping .swift files into there–no configuration or manifest changes necessary.

📁 MyPackage
  📄 Package.swift
  📁 Sources/
  📁 Tests/
  📁 Snippets/
    📄 A.swift
    📄 B.swift
    📄 C.swift
    ...

As you can see, the simple case (above) is incredibly simple to get started. As your body of snippets grows, you can create subdirectories to serve as groups, and even add an Explanation.md file that provides an abstract for the group.

📁 MyPackage
  📄 Package.swift
  📁 Sources/
  📁 Tests/
  📁 Snippets/
    📁 Fizzing
      📄 Explanation.md
      📄 Fizz.swift
      ...
    📁 Buzzing
      📄 Explanation.md
      📄 Buzz.swift
      ...

Browsing a package's snippets at the command line

Included in the initial code contribution is an interactive command line experience that presents snippets in menus, or cards, along with short explanations. Here's what it looks like.

First, below is the top card. This shows either the snippets you have put into the Snippets directory or snippet groups, if you have opted to create those.

Here below is the snippet group card. Grouping your snippets is optional, but it can help you organize your snippets by task or concept.

Finally below is the card for a snippet. You can see the abstract, code, and an option to run the snippet.

Try it out!

Snippets are available for testing in the Trunk Development snapshots. To try out the interactive experience at the command line, you first need some snippets. I've created a dummy project for you to see the folder structure, which you can clone and try the command below or start adding them to your own packages.

Once you've got some snippets in your Snippets directory, set an environment variable SWIFTPM_ENABLE_SNIPPETS to enable the feature and use the new swift-package subcommand called learn.

export SWIFTPM_ENABLE_SNIPPETS=1 

swift package learn

Questions?

Here are some answers to potential questions you might have about snippets:

Why single files?

Example code projects are still useful vehicles for explaining complex applications that may pull in many concepts at the same time. However, they can grow to be quite large and difficult to maintain. They also present navigation challenges for beginners and experienced developers alike. SwiftPM already has executable targets in which many source files can take part. Snippets are a short path to creating digestable, searchable chunks of code.

Are snippets just tests?

No, snippets aren't tests. Again, snippets are a teaching tool, and I wanted to provide a tool that lets package authors focus on teaching users how to use their package and not mix in complex concerns about setup, tear-down, and glue code that is not typical of real world use.

Future Development

In the future, here are some aspects I would like to explore with snippets:

  • Add syntax highlighting to code snippets at the command line. This is something that I have already started working on but will require a SwiftSyntax dependency.

  • Presentation order of snippets and groups

  • Export of snippet data for integration into DocC, The Swift Programming Language book, swift.org, and more

  • Search for text and titles at the command line

  • Turning snippets into high-level templates

  • Creating a style guide for teaching with snippets

  • Optional or uncommon features for Package.swift, such as excludes

I hope I've whetted your appetite and I'm curious to hear what you think about this approach.

Thanks! :white_small_square:

40 Likes

Would resources be something snippets should have access to? How does a Snippet process them etc? e.g.: teaching image manipulation APIs?

1 Like

Yes I think snippets should have access to resources already although not with any formal or explicit mechanism with the hopes of keeping it simple and as low resistance as possible. Right now, that means that related snippet resources should exist under the Snippets subdirectory, ideally next to the related snippet, and found by relative file path.

  1. does these snippets get vended as part package under Swift package registries?

  2. could the Swift package snippet feature support Swift playgrounds files?

  3. I really like this. I wonder if it could be all be in a mark down files with Swift code blocks that the package manager parses to vend the snippet, and docc artifacts. Something similar to GitHub - fastai/nbdev: Create delightful software with Jupyter Notebooks

This might be naive of me to ask but... how does this proposal add value differently from Xcode Playgrounds?

It could add value for non-Apple platforms.

2 Likes

The idea is interesting, thank you for exploring this field. That being said, DocC has been advertised as the best tool for generating package documentation and related learning experiences in general. Snippets can be added in a proper DocC Article or, depending on the circumstances, in a DocC Tutorial. I would expect a swift package learn command (if it existed) to serve the generated DocC HTML pages and open their main page in the user browser.
DocC depends on Xcode, but if there's a chance to make it standalone and platform independent, I would happily prefer its integration in SPM over a command line interactive experience.

Edit: I didn't mean to reply to @davdroman, this post refers to the main topic. Sorry about that :sweat_smile:

1 Like

Snippets are intended to be complementary to DocC, where snippets could be imported (in the future) as samples presented within DocC documentation. This would allow CI to build and test the snippets to ensure the docs continue to present valid sample code, and identify when sample code needs to be updated. This is an early implementation, of course, with many cool ideas down stream starting from a very simple starting point.

8 Likes

Yes, a really important goal for Snippets is to work across all supported platforms. A big goal here is to start from the simplest possible case, and provide a super-easy method for package authors to share snippets that show off how to use their package. Would be great if everyone in the Swift ecosystem writing packages has a simple, standard way to show off their code, regardless of platform or use case.

5 Likes

This is brilliant.

Thank you for explaining :slight_smile:

Hi Cheyo!

As currently implemented, the snippets travel along with the package’s sources so, as long as those can be reached, the information about snippets could be packaged up in any number of ways depending on the environment. I see a package’s snippets existing both as a part of its documentation as well as some sort of “showcase” or accessible through some other direct way. Either of those things would make a lot of sense for a package registry.

That’s an interesting idea, thanks for the link! This is something I have thought about a lot in the past while working on DocC. I think my view right now is that you need both forms, Swift-inside-Markdown (like a DocC article) and Markdown-inside-Swift (like a doc comment), in order to easily tap the strengths of the respective formats. Since these are meant to be brief and not tutorial-like, I think the code will slightly outweigh the prose on these.

1 Like

The fact that snippets are executed (not just described) by the browser is somewhat underplayed in your description; you could promote that in the title perhaps.

These sound like CRAN vignettes.

Very cool

1 Like
  • And the same //#-hidden-code and //#-end-hidden-code delimiters?
  • Basically, can we use the existing playground editors for snippets?
  • README.md would be better for browsing on GitHub, etc.
  • Or should the explanation be in //: and /*: comments?
  • Should the subcommand be named after the feature and its directory?
  • e.g. swift package snippets or swift run --snippets
2 Likes

I think the style of comment marker isn't too important as long as it comes across as different. The ! was just a placeholder. We will probably bikeshed things like this further in the future but don't want to get caught up in syntax just yet.

As for Markdown, I would expect it to eventually have the same capabilities as doc comments today, at least all of CommonMark, so that DocC and the Web can pick these up with parity.

It's possible that in the future these will interact with Playgrounds tooling and infrastructure but I don't want to design around that so all platforms can benefit equally at the inception. Not to worry, it is definitely in mind!

Yes, I was thinking README could work although it feels very 'top-level' to me for some reason whereas these will be pretty frequent. This might be a good place to continue with the tradition, though.

The group explanations shouldn't be in doc comments because they may explain multiple files and no single .swift file should necessarily take ownership of that. It's generally best to write Markdown outside of doc comments to keep the tooling as flexible as possible.

Possibly, although this command will probably include other learning features for packages in the future. For example, typing swift learn mutating could certainly do something useful :). It's also possible to add a shortcut command to go straight to snippets as well. It should become more clear in the near future.

Thanks for your questions and comments!

1 Like

For some reason, I didn't grok the benefits of this pitch when I first scanned through it - fixating a bit more on the CLI interactive component. While handy and interesting, in a re-read today I am far more excited about the potential to provide a way to have documentation examples inline with a project, and verify that they keep compiling, or show relevant errors or warnings when your code hits a deprecation use case.

I'm doubly thrilled at the include the mechanism (MARK in this case) that lets you separate setting up the example from showing the example. I've hit that myself repeatedly, and have a convention for my own use, but that doesn't cleanly parse for external systems (such as DocC), so I'm very excited to have a specific mechanism.

Finally - I know it's looking farther ahead than you're proposing with this pitch, but I'd love to also have a clear way to make sure each example snippet that may be presented (e.g. each card) has a clear identifier so that some future mechanism could explicitly curate the examples for presentation and even cross referencing (such as in externally rendered documentation), and not just relying on a default ordering from the internal systems.

2 Likes

Absolutely, this is actually near or at the top of the list for next steps. For sure we will want to be able to directly pick out specific snippets. It will likely be something like the relative path from the snippets directory including the filename (with or without extension), within the “namespace” of the package.

Also considering using an informal naming scheme to order snippets and groups using filenames without the need for explicit curation in an external source. But DocC will still need a way to curate or inline via links as needed. Stay tuned for that.

4 Likes

Just trying to add Snippets support for swift-markdown Add simple snippets support by Kyle-Ye · Pull Request #70 · apple/swift-markdown · GitHub.

I was wondering when can we get some sort of support from the IDE not just the termial interface.

Maybe Xcode(waiting for Xcode team to support this GUI feature or maybe we can use the existing interface of XcodeProjectPlugin to support it) or VSCode Plugin could help on this (via GitHub - swift-server/vscode-swift: Visual Studio Code Extension for Swift)

I've filed a feature request issue here Add Swift Snippets GUI support in VSCode · Issue #391 · swift-server/vscode-swift · GitHub

1 Like