Two tips I got from Apple engineers to avoid Memory Leaks and increase efficiency in SwiftUI

Yugantar Jain
Dev Genius
Published in
3 min readJul 15, 2020

--

Avoiding retain cycles and building efficient views in SwiftUI

Hi everyone,

In today’s article, I share two important tips that I got directly from Apple engineers during my labs at WWDC20.

  1. Avoiding Memory Leaks: Avoid retain cycles caused by capturing of self in a closure in a class.
  2. Increasing efficiency of optional views in SwiftUI: Avoid using AnyView and use @ViewBuilder instead.

1. Avoiding retain cycles caused by capturing ‘self’ in a closure in a class

Swift uses Automatic Reference Counting (ARC) to manage its memory allocation and deallocation. This means that Swift automatically allocates memory to objects and then frees up that memory when the object is no longer in use (this is done by tracking the number of references of each object, zero references lead to deallocation).

While ARC manages most of the memory management, a strong reference cycle can lead to memory leaks from your app.

A common use case for such kind of a retain cycle is when self is captured inside a closure in a class.

“A strong reference cycle can also occur if you assign a closure to a property of a class instance, and the body of that closure captures the instance.” — Swift.org

In the Mentorship iOS app, we had these retain cycles while making the network calls and assigning properties in the sink operator.

A closure, like a class, is a reference type. Hence, by capturing self inside a closure in a class we make a retain cycle; since the closure is captured by the class and the class is captured by the closure. As a result, in the above code, the class Members will never be deinitialized (memory leak!).

Solution:

The strong reference cycle can be easily solved by capturing weak self in the closure.

.sink { [weak self] members in
self?.membersResponseData = members
}

2. Avoiding AnyView in SwiftUI to increase efficiency

SwiftUI uses Opaque Types to let us build the views without explicitly mentioning the exact view type. For this to work, SwiftUI demands that a view should return a definite single type.

This soon becomes a thing to be solved, a common example of which is when we want to optionally show different views.

var body: some View {
if caseOne {
viewOne()
} else {
viewTwo()
}
}

In the code snippet above, SwiftUI isn’t able to infer at the compile time a definite view type and throws an error.
*Note: this will work in Xcode 12 (explained below)

A common workaround to this problem is to use AnyView, which type erases a view and hence satisfies the opaque type.

However, AnyView is not efficient since it recreates the whole hierarchy every time. This is a rather important point and was also highlighted by Josh Shaffer and Eliza Block in this amazing post-WWDC20 podcast with John Sundell.

Solution:

The recommended solution to this is to use the @ViewBuilder property wrapper.

In Xcode 12, @ViewBuilder will be automatically inferred and won’t even need to be specified in most cases. However, for the time being, this is how to use it —

@ViewBuilder var body: some View {
if caseOne {
viewOne()
} else {
viewTwo()
}
}

Thank You for Reading! I hope you liked it :)

Please feel free to reach out to me and connect, check out our projects, or join our open source community:
LinkedIn, GitHub, Mentorship iOS, AnitaB.org Community

--

--