Go to homepage

Reid Main

  1. Archives
  2. Tags
  3. About Me
  4. Email
  5. Resume

πŸ”— The Future Of [weak self] Rebinding

For a few major Swift versions the following code is allowed to work with weakly captured self:

guard let `self` = self else { /* … */ }
// proceed with non-optional self

It’s a wide-spread pattern today. The consensus from past discussions is that this has been a compiler bug. Ways to fix it have been suggested.

I am a huge fan of this "compiler bug" because it is what the Swift language naturally pushes developers towards. The concept of guarding against something you want is drilled into Swift developers and one of the most common things you want to guard against is the [weak self] you should add to almost every closure you create.

The alternative solution to this problem is usually something along the lines of

someFunctionCall { [weak self] result in
    guard let strongSelf = self else {
        return
    }

    // Remember to use strongSelf instead of self inside the closure.
}

which I am not a huge fan of because it still allows developers to use the weakly referenced self inside the closure. Sure they won't accidentally create a retain loop but you will inevitable have developers who forget to use strongSelf for a myriad of reasons and gunk up your closure with all sorts of optional checks and unnecessary optional chaining. You can combat this with thorough code reviews but unless you have draconian linting (which is very difficult in Swift currently) this sort of abuse will find a way to slip through the cracks.

I believe the answer to this problem is that self should be weakly retained inside all closures and some sort of [guard self] language feature could be added to allow us to quickly strongify self.

With my solution the above example would become:

someFunctionCall { [guard self] result in
    // Proceed with a non-optional self.
}

One major shortcoming with this solution is that you cannot react if self has become nil. A fix for that could be the ability to pass some closure to [guard self] such as

someFunctionCall { result in
    [guard self] {
        // Do something if self has somehow become nil.
    }

    // Proceed with a non-optional self.
}

While I realize there are dozens of other performance and stability problems with Swift that are of higher priority, I hope that issues like this do not fall to the wayside for too long. Workarounds for this design shortcoming in the Swift language are already polluting tens (if not hundreds) of thousands of projects and the longer it goes unaddressed the harder it will be to migrate to the universally accepted solution.