Articles, podcasts and news about Swift development, by John Sundell.

Two ways of capturing self strongly within a closure

Published on 02 Jan 2022
Basics article available: Closures

In Swift, there are two ways to capture self as a strong reference within an escaping closure. The first is to explicitly use the self keyword whenever we’re calling a method or accessing a property on the current object within such a closure.

For example, the following VideoViewController performs such a strong capture in order to be able to call two of its own methods whenever it finished preparing its Video model:

class VideoViewController: UIViewController {
    private var video: Video
    ...
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        prepareVideo {
            self.movePlayhead(to: self.video.lastPlayheadPosition)
self.startPlaybackIfNeeded()
        }
    }
}

The reason that it’s safe to capture self strongly within the above example is because the closure that we pass to prepareVideo isn’t stored in a way that would potentially cause a retain cycle. For more on that topic, check out “Is using [weak self] always required when working with closures?”.

The above is certainly the most well-known way to access properties and methods on self within an escaping closure. But there’s also another, lesser-known technique that lets us reference self just once — and that’s to use a capture list to set up our reference. While capture lists are most commonly used when we want to create a weak or unowned reference to self, or when we want to capture a specific set of properties, they can also be used in situations like the above in order to avoid having to retype that same self prefix multiple times — like this:

class VideoViewController: UIViewController {
    private var video: Video
    ...

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        prepareVideo { [self] in
            movePlayhead(to: video.lastPlayheadPosition)
            startPlaybackIfNeeded()
        }
    }
}

Note how we can now call methods and access properties on self just like if we were writing our code in a context other than an escaping closure, which is quite neat!

Of course, we also always have the option to move the logic that we want to perform to a dedicated method instead, which we could then simply call from within our closure:

class VideoViewController: UIViewController {
    private var video: Video
    ...

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        prepareVideo {
            self.videoWasPrepared()
        }
    }
    
    private func videoWasPrepared() {
        movePlayhead(to: video.lastPlayheadPosition)
        startPlaybackIfNeeded()
    }
}

It’s important to re-iterate that the above techniques are specifically for creating strong references to objects. When we don’t want to retain the current object, we’d be much better off using either a weak or unowned self capture, or to avoid capturing self entirely. For more on that topic, check out “Swift’s closure capturing mechanics”. Also, when working with value types, the compiler will implicitly capture self for us, which you can read more about here.

Thanks for reading, and Happy New Year! 🎉