Copilot for Xcode Works Okay

I’ve never touched GitHub Copilot in all these years, but everyone seems to be very happy with it. People recommend Copilot for all kinds of refactorings and repetetive tasks.

So I figured I might give it a try and see how it works.

Just yesterday, I used the Copilot Xcode plugin to write a lot of boilerplate for me. I can confirm it does its job.

https://github.com/intitni/CopilotForXcode

I used it to help me write a unit test spy for an umbrella repository, or façade, that conforms to 5 protocols. In my human mind, there was rhythm and similarity, but code completion can’t help there.

Let’s say I needed to implement this:

protocol JungleRepository {
    func trees(in jungleID: JungleID, limit: Int?) throws -> Tree

    func bananas(treeIDs: Set<TreeID>) throws -> [Banana]

    func monkeys(
      treeIDs: Set<TreeID>,
      withBananas bananaIDs: Set<BananaID>
    ) throws -> [Monkey]

    func monkey(monkeyID: MonkeyID) throws -> Monkey
}
  • The methods follow a similar-ish pattern, but they are not the same.
  • The parameter lists differ in type and arity (parameter count).

Writing a spy would start as:

class JungleRepoSpy: JungleRepository {
    // In this case, the spy should record method invocations
    // and then delegate to an actual repository for the
    // real action. (Could be a stub.)
    let base: JungleRepository
    init(base: JungleRepository) {
        self.base = base
    }

    /// Recorded parameters of calls to ``tree(treeID:)``.
    var trees_jungleID_limit_calls: [(jungleID: JungleID, limit: Int?)] = []
    func trees(in jungleID: JungleID, limit: Int?) throws -> Tree {
        trees_jungleID_limit_calls.append((jungleID, limit))
        return try base.trees(in: jungleID, limit: limit)
    }

    // TODO: The rest :O
}

The spy’s implementation consists of:

  • One property per method to record the calls of the method, using the list of parameters,
  • A method implementation that forwards to a base repository (so the spy doesn’t have to implement e.g. stubbed default values on top; spying is a full-time job already!), and
  • which appends the parameter list as a tuple to the ‘calls’ property.

That’s an easy checklist for a human, but you can’t copy and paste this over and over. The property name needs adjustment, the parameter list is different and so is the array of tuples. It’s boring work for us very intelligent humans.

Copilot did a great job with this.

  1. Before the function, type var, trigger Copilot, and it’ll suggest a similarly-named variable.

    It’s not perfect, because you can’t have tuples for 1 value only. Correct it this time, and it won’t make the mistake next time.

  2. Type func, trigger Copilot suggestions, and it’ll suggest the function implementation with all the boilerplate.

Repeat var, suggest, func, suggest until the protocol is implemented.

I can absolutely understand why the rest of the team loves Copilot for this kind of work.


Am I now complicit in exploiting closed-source authors whose code GitHub stole? Or GPL’ed code that is copy-and-pasted without attribution and without GPL’ing my own code, thus violating the license?

Please rest assured for now: I didn’t use this any further and I didn’t keep the spy.

But I am now absolutely interested in experimenting with self-hosted, open source LLM’s. I know of open source ChatGPT alternatives that folks recommended, and I expect Copilot-like projects to spawn, but I don’t yet know of any. The completion/suggestion UI is simple enough to make copycats possible. Getting a good model is now the actual complicated task. (How do you beat GitHub/Microsoft at this, or come close?)