When to write `self` in Swift
Or must the dinosaurs die?
The other day I encountered on “you forgot to put self before property” comment in my pull request. Did I? Or it’s actually better to omit unnecessary self
? Let's try to figure out.
One of the really shining sides of the Swift is its brevity. The developer could write really compact code omitting a lot of boiler-plate thanks to type inference, synthesized conformance to standard protocols (e.g. Codable, Equatable), trailing closure syntax, you can continue this list if you want. But as uncle Ben said: “with great power comes great responsibility” or Swift API Design Guidelines say: “clarity is more important than brevity”, so don’t overuse these little features.
Reasons to write self
Indeed, the main reason to put self
when compiler lets you omit self
is it should be clear whether a piece of code accessing instance variable or local variable.
For example, we see such a piece of code:
func events(at date: Date) -> [Event] {
if let events = cache[date] {
return events
}
let events = fetchEvents(at: date)
cache[date] = events
return events
}
Somewhere deep in mind we understand that cache
and fetchEvents
are instance variable and method, but we not 100% sure. It because Medium doesn’t support syntax highlighting. When you would see same code in your favorite IDE it would be something like this:
All your doubts are gone.
When we prepend all instance variables and methods with self
we would solve the problem for environments without syntax highlighting such as GitHub, Medium, name it.
func events(at date: Date) -> [Event] {
if let events = self.cache[date] {
return events
}
let events = self.fetchEvents(at: date)
self.cache[date] = events
return events
}
So, to omit self makes it harder to reason about code for color blinded people and when you’re reading code outside IDE.
But there is another reason to put self
on every corner: when you’re dinosaur with huge baggage of Objective-C experience and can’t accept rules of new game. Or you’re really lazy and you’ve already been taught some weird guidelines.
There was even Swift evolution proposal SE-0009 to make self
required but it was rejected. Let’s think why.
Reasons not to write self
I can count two reasons when the Swift compiler forces developer to put self
before instance variables.
- to avoid ambiguity, for example in initializers
- to avoid accidentally create a strong reference cycle in escaping closure
With first reason all crystal clear, take a look at this snippet:
struct Person {
let name: String init(name: String) {
self.name = name
}
}
Indeed, compiler should distinguish which name
is local and which is instance variable.
Second reason has a special consideration in Swift Docs, I hope you read them, if no go to do it right now.
An escaping closure that refers to
self
needs special consideration ifself
refers to an instance of a class. Capturingself
in an escaping closure makes it easy to accidentally create a strong reference cycle.Normally, a closure captures variables implicitly by using them in the body of the closure, but in this case you need to be explicit. Writing
self
explicitly lets you express your intent, and reminds you to confirm that there isn’t a reference cycle.
Previously self
needed special consideration in all escaping closures but in Swift 5.3 includes changes SE-0269 to increase availability of implicit self
in escaping closures when reference cycles are unlikely to occur. And now compiler would give you an error only when self
refers to an instance of a class.
If you’re making a habit to spread self
all over the place you are making yourself blind and all attempts of guys from Cupertino to make your life easier go to hell.
I’ve asked several developers at Twitter whether they write self
where they don’t have to. Most of them write self
as little as possible, one of them event punish his teammates for overuse of self
. Only one responded that he used to write it everywhere, cause it makes code review easier.
Let’s summarize.
We put self
everywhere if doctor tells you so, if it helps you to conduct a code review or if you’re an ignorant dinosaur.
We put self
as little as possible if we want to make code compact, reduce amount of cognitive load, virtually to reduce time to write code, and mostly to help compiler to save you from accidental mistakes.
Thanks to make it till the end. I’m curious what do you think about this and what your team practice, don’t be shy to leave the comment.