Thinking about AI assisted coding

Have you heard about all the buzz about AI lately?

I did. Why do you ask?

Well, I was wondering if it can help me with making my life easier as a software developer.

I’ve heard about GitHub Copilot, but never used it, since there is no support for Xcode (yet).

If you had Xcode support for AI assisted coding, how would you use it?

Good question. I am exploring TDD lately, so I would probably start with that. I would write tests and ask my AI assistant to make them pass.

That sounds wonderful.

It truly does. Let’s explore the idea. For this demonstration purpose we will use ChatGPT. I’ve played around with it and it can really do some interesting things. Shall we write some tests?

Go for it!

Ok. First the requirements: Create a composite worker that executes child workers concurrently and completes when all children complete.

Simple enough.

Swift
import XCTest

protocol Worker {
  func doWork(completion: @escaping () -> Void)
}

final class ConcurrentWorker: Worker {
 
}

final class ConcurrentWorkerTests: XCTestCase {
  func test_init_doesNotMessageChildren() {
    let childWorker1 = WorkerSpy()
    let childWorker2 = WorkerSpy()
    _ = ConcurrentWorker(children: [childWorker1, childWorker2])
    
    XCTAssertEqual(childWorker1.doWorkCallCount, 0)
    XCTAssertEqual(childWorker2.doWorkCallCount, 0)
  }
  
  func test_doWork_messagesChildren() {
    let childWorker1 = WorkerSpy()
    let childWorker2 = WorkerSpy()
    let sut = ConcurrentWorker(children: [childWorker1, childWorker2])
    
    sut.doWork { }
    
    XCTAssertEqual(childWorker1.doWorkCallCount, 1)
    XCTAssertEqual(childWorker2.doWorkCallCount, 1)
  }
  
  func test_doWork_completesWhenAllChildrenComplete() {
    let childWorker1 = WorkerSpy()
    let childWorker2 = WorkerSpy()
    let sut = ConcurrentWorker(children: [childWorker1, childWorker2])
    
    let exp = expectation(description: "Work should complete")
    sut.doWork { exp.fulfill() }
    
    childWorker1.complete()
    childWorker2.complete()
    
    wait(for: [exp], timeout: 0.1)
  }
  
  func test_doWork_completesOnProvidedQueue() {
    let queue = DispatchQueue(label: "test.queue")
    let queueToken = DispatchSpecificKey<()>()
    queue.setSpecific(key: queueToken, value: ())
    let sut = ConcurrentWorker(children: [], completionQueue: queue)
    
    let exp = expectation(description: "Work should complete")
    sut.doWork {
      XCTAssertNotNil(
        DispatchQueue.getSpecific(key: queueToken), 
        "The completion should be executed on the provided queue.")
      exp.fulfill()
    }
    
    wait(for: [exp], timeout: 0.1)
  }
}

final class WorkerSpy: Worker {
  private(set) var completions = [() -> Void]()
  var doWorkCallCount: Int { completions.count }
  
  func doWork(completion: @escaping () -> Void) {
    completions.append(completion)
  }
  
  func complete(at index: Int = 0) {
    completions[index]()
  }
}

Ok, now that we have the tests ready, let’s ask GPT to implement the `ConcurrentWorker`.

Then copy the full test code above and voilĂ :

Swift
final class ConcurrentWorker: Worker {
  private let children: [Worker]
  private let completionQueue: DispatchQueue
  
  init(children: [Worker], completionQueue: DispatchQueue = .main) {
    self.children = children
    self.completionQueue = completionQueue
  }
  
  func doWork(completion: @escaping () -> Void) {
    let dispatchGroup = DispatchGroup()
    
    children.forEach { worker in
      dispatchGroup.enter()
      worker.doWork {
        dispatchGroup.leave()
      }
    }
      
    dispatchGroup.notify(queue: completionQueue, execute: completion)
  }
}

That is amazing! Now we just have to figure out how to make an Xcode plugin out of it.

Indeed, but that is a topic for next time.