GraphQL with Swift

Know that thing where you start writing a tool for something, discover it needs (for some values of “need”) something slightly complicated, you decide to write a library for said complicated thing, then discover you don’t need the tool you were working on in the first place but decide to write the library anyway?

So I wrote GraphQLer (pronounced “graph quiller”), a library for generating GraphQL from Swift. It’s not the only Swift GraphQL library around, but I believe it’s far simpler — mostly by being far less ambitious — than the other solutions. Throwing in the kitchen sink seems to be popular in this space, forcing you to shape your code around the libraries. That may work out great in some situations, but it wasn’t what I wanted.

GraphQLer just defines a bunch of data types that model the things found in the spec, and functions for turning those data types into strings. Its only purpose is to make it easier to write syntactically valid GraphQL documents. It has no dependencies — it doesn’t even import Foundation — and you handle all networking and responses as you see fit.

It works with the Swift Package Manager. There’s also a Xcode workspace and a playground, but you need to run SPM to get an Xcode project. I'll add an Xcode project if I have to to make it work with Carthage, but the WWDC 2019 keynote is tomorrow and I'm hoping there will be surprises related to SPM.

PS. A shout out to jazzy and GitHub Pages: it's ridiculous how easy it is to publish documentation for a Swift project these days.

annotate-git-commit

I got into the feature branch workflow back when I was using Mercurial. I’ve since discovered that Mercurial’s branches aren’t really suitable for short-lived branches and switched to Git for a variety of reasons, but I still wish my commits carried with them metadata about what they were related to.

I also still wish my VCS had a sane UI, but that’s an unrelated topic.

Anyway, my Git branches, when I’m working with a ticketing system, usually carry in their name a ticket identifier in an easily parseable format. I wrote a small too for extracting that ticket identifier and pasting it to the bottom of my commit messages: annotate-git-commit. You can use it with Git’s prepare-commit-msg hook.

TODO.swift

I saw this Swift trick a few years ago on Twitter. I can’t recall who it was, sorry. It sounded way too clever at first, but after a while I tried and decided I love it. Now it’s one of those things I add to every project.

Sometimes you just want to get on with declaring your functions without worrying about the actual implementations. A small enough function with good enough name and types is pretty much done. Filling out the body is just something that will distract you and make you lose the big picture by bogging you down in the details.

The problem with incomplete functions is that they make the type checker unhappy. Have a return type but no return statement? That’s a compilation error. And the more interesting your types, the harder it is to make a temporary hack to placate the compiler.

Swift offers an escape hatch: the Never type. Call a function that returns Never and you don’t need to worry about writing a return. So this will compile:

func foo(zap: Bar) -> Fnord {
    fatalError("Unimplemented")
}

The code will compile, it will just crash at run time. Exactly what we wanted. But we can do better.

You can write your own function that returns Never as long as you ensure it never returns. Like, for example, by calling fatalError as we did in the previous example:

func TODO() -> Never { fatalError("Unimplemented") }

func foo(zap: Bar) -> Fnord {
    TODO()
}

That’s an clear improvement: less need for repeating yourself, and easier to find in the code. The next small step will be to ditch the parentheses by switching to a computed property:

var TODO: Never { fatalError("Unimplemented") }

func foo(zap: Bar) -> Fnord {
    TODO
}

Not a huge improvement, but better, anyway.

There’s one more thing we can do: we can make it harder to forget these in the code. TODO doesn’t cause errors, but we can make it cause a warning by abusing Swift’s deprecation annotation. And because you care for the quality of your code base you fix all warnings and so can’t help but notice the new ones when you compile.

@available(*, deprecated, message: "Unimplemented")
var TODO: Never { fatalError("Unimplemented") }

func foo(zap: Bar) -> Fnord {
    TODO
}

Now the TODO line inside Foo will cause a warning:

'TODO' is deprecated: Not implemented

You’ll get a nice warning reminding you to implement the function before merging your code in, but you can do it when you’re ready.

© Juri Pakaste 2024