Celebrating 10 Years!

profile picture

How To Write IRC: Part 1

August 20, 2017 - Roundwall Software

IRC is a fairly old protocol for chatting with people on the internet. It's not super popular these days because even more technical people have migrated to things like Slack or Hipchat which may or may not be more friendly to use. Still, I decided to try to write a library for interacting with IRC for a few reasons:

  1. I think a big reason for Slack/Hipchat popularity over IRC is the clients. Every IRC client I've ever tried seems fairly technical and does not try to simplify the experience very much.
  2. The library and my writing explaining it can serve as nice example of how to make a framework and how to write Swift in general using tests. Even if it never gets used in an exciting IRC client.
  3. The library can also serve as an example for implementing support for an internet protocol that isn't already supported for the system (like HTTP(S) is already supported).

Once I was able to rationalize spending time on this myself, I decided on the features for the initial milestone:

  1. It needs to connect to IRC servers.
  2. It needs to allow joining channels.
  3. It needs to allow sending and receiving messages on those channels.

There are different strategies for making frameworks, but this is how I do it:

  1. First, make IRC code work in a demo app.
  2. Second, extract out the IRC bits into a framework that's usable outside the demo app
  3. Third, forget about the demo app because it's probably ugly.

API options

For my implementation of an IRC client library, I decided to try to use the new URLSessionSteamTask API which they seem to want us to use instead of older NSSTream APIs or directly through Grand Central Dispatch which I've tried to use before. I previously thought the GCD way wasn't too bad, but this new stuff was even easier.

There's not much to the API, you can ask a URLSession to make a streaming task with

streamTask(withHostName: "irc.freenode.org", port: 6667)

only you'd need to put in your desired hostname and port, not mine. (If you're not sure what a hostname or port is, no worries, maybe read this article first.) From there you have a task to resume like any other URLSessionTask except this one lets you also schedule reading data or writing data on the URLSession's queue. To read from your connection, you say

readData(ofMinLength minBytes: Int, maxLength maxBytes: Int, timeout: TimeInterval, completionHandler: @escaping (Data?, Bool, Error?) -> Swift.Void)

which tells the task how much data to read, how long to wait, and lets you provide a closure to do whatever you like with the data once it's ready. Writing is very similar,

write(_ data: Data, timeout: TimeInterval, completionHandler: @escaping (Error?) -> Swift.Void)

which tells the task what data to send, how long to wait, and a closure to do something if there's a problem. With this, some explanations of the IRC protocol, and a bunch of unit tests, I set out to build an IRC framework.

The work continues in How To Write IRC: Part 2