Using Threads in Swift

Swift provides DispatchQueue as an excellent layer above raw threads. But sometimes you want to create a new thread dedicated to some specific task. Or maybe implement your own concurrent executor. Swift gives you access to raw threads and in this article, I'll show how to use it.

Thread

Creating a thread in Swift is pretty simple using Thread class. You can either specify objc function through a selector as a starting point, or pass a closure, and, more convenient way, subclass Thread.

class MyThread: Thread {
    override func main() { // Thread's starting point
        print("Hi from thread")
    }
}

let thread = MyThread()
thread.start()
Simple thread

The thread is not started when the initializer is called. You need to call start()  method explicitly to start the thread.

The thread runs despite its handle returned by Thread initializer. That's it — the variable can no longer exist and the thread will still run. That's fine, but you will lose the ability to control the thread: check if it's completed, wait for its completion, cancel it, etc.

Wait for completion, join a thread

Swift does not provide a way to wait for the thread's completion.

💡
The main thread can finish before the new thread. In this case, the latter is also terminated

To wait for thread completion, we can join threads using DispatchGroup

class MyThread: Thread {
    let waiter = DispatchGroup()

    override func start() {
        waiter.enter()
        super.start()
    }

    override func main() {
        task()
        waiter.leave()
    }

    func task() {
        print("Hi from thread")
    }

    func join() {
        waiter.wait()
    }
}

let thread = MyThread()
thread.start()

thread.join() // Waits for thread completion

Terminate the thread

The thread terminates automatically after reaching main's end. To exit the thread in advance, you can call Thread.exit() function from the thread. To use it correctly with created DispatchGroup, it's better to create a custom exit method:

class MyThread: Thread {
    ...
	func exit() {
        waiter.leave()
        Thread.exit()
    }
    ...
}

Cancel the thread

Apart from terminating the thread, you can cancel it, by calling cancel() method on the thread's handle or inside the thread itself. This sets isCancelled property to true.