A new Swift video series exploring functional programming and more.
#54 • Monday Apr 15, 2019 • Subscriber-only

Advanced Swift Syntax Enum Properties

This week we’ll put the finishing touches on our enum property code generation tool. We’ll add support for enum cases with multiple associated values and enum cases with no associated values, and we’ll add a feature that will make enums even more ergonomic to work with!

#54 • Monday Apr 15, 2019 • Subscriber-only

Advanced Swift Syntax Enum Properties

This week we’ll put the finishing touches on our enum property code generation tool. We’ll add support for enum cases with multiple associated values and enum cases with no associated values, and we’ll add a feature that will make enums even more ergonomic to work with!


Subscribe to Point‑Free

This episode is for subscribers only. To access it, and all past and future episodes, become a subscriber today!

See subscription optionsorLog in

Sign up for our weekly newsletter to be notified of new episodes, and unlock access to any subscriber-only episode of your choosing!

Sign up for free episode

Previously

Last time we set out to automate all the boilerplate involved in creating what we call “enum properties”, which are computed properties on enums that give us easy access to the data held inside the enum’s cases. We’re doing this because Swift makes it very easy to access data in structs, but does not give us many tools for doing the same with enums.

To achieve this we started creating a package with the Swift Package Manager. It has a dependency on SwiftSyntax, which is Apple’s library for parsing Swift source code into a tree of tokens that we can traverse and inspect. We ended up with a playground that is capable of parsing the code in a Swift file, finding all the enums in it, and creating computed properties for each of the cases in those enums.

That’s awesome, but there are still a lot of subtle edge cases that we are not accounting for, and so we can’t really point this tool at a real world code base and expect it to work.

So today we are going handle more of those edge cases so that we will be in a good position to extract everything out of the playground and make a real command line tool out of it!

Let’s take a look at what we’ve written so far.

import SwiftSyntax
import Foundation

let url = Bundle.main.url(forResource: "Enums", withExtension: "swift")!
let tree = try SyntaxTreeParser.parse(url)

class Visitor: SyntaxVisitor {
  override func visit(_ node: EnumDeclSyntax) -> SyntaxVisitorKind {
    print("extension \(node.identifier) {")
    return .visitChildren
  }

  override func visitPost(_ node: Syntax) {
    if node is EnumDeclSyntax {
      print("}")
    }
  }

  override func visit(_ node: EnumCaseElementSyntax) -> SyntaxVisitorKind {
    print("  var \(node.identifier): (\(node.associatedValue!.parameterList))? {")
    print("    guard case let .\(node.identifier)(value) = self else { return nil }")
    print("    return value")
    print("  }")
  }
}

let visitor = Visitor()
tree.walk(visitor)
// extension Validated {
//   var valid: Valid? {
//     guard case let .valid(value) = self else { return nil }
//     return value
//   }
//   var invalid: [Invalid]? {
//     guard case let .invalid(value) = self else { return nil }
//     return value
//   }
// }

Now this tool is very useful but currently only works for enums where every case has a single associated value. In the case of multiple associated values or no associated values, we would be generating invalid code. So today we’re going to address a bunch of these shortcomings to end up with a tool that we can run on code bases with many different kinds of enums in various shapes that should always generate correct, compiling code.

Subscribe to Point-Free

👋 Hey there! Does this episode sound interesting? Well, then you may want to subscribe so that you get access to this episodes and more!

Chapters
Previously
00:05
Supporting multiple associated values
03:09
Supporting no associated values
12:24
Adding boolean properties
23:09
What’s the point?
27:28