The Sneaky Coordinator Leak in UIViewRepresentable
Let's talk a little about a memory leak I recently found while working with UIViewRepresentable. The issue happened in a custom UIKit view wrapped inside SwiftUI. At first, nothing looked suspicious. The view worked fine. But after navigating in and out of the screen a few times, the Memory Graph Debugger showed that the Coordinator instances kept increasing. Here is a simplified version of the…
In a recent discovery while working with UIViewRepresentable, a memory leak was identified in a custom UIKit view embedded within SwiftUI. The issue arose after navigating in and out of the screen multiple times, causing the memory usage to progressively increase, as evidenced by the Memory Graph Debugger.
The custom view was a simple CallbackButton, featuring an onTap method that triggered the handleTap function within the Coordinator. The Coordinator class held a strong reference to the binding title, which was then passed to the UIViewRepresentable through the makeCoordinator function.
The problematic line of code was: button.onTap = context.coordinator.handleTap. Although it appeared as if only a function was being passed, the closure still maintained a strong reference to the Coordinator, leading to the memory leak. In a typical UIKit view controller, this issue would have been resolved with a weak self capture, but UIViewRepresentable is a struct, making the use of [weak self] different.
To rectify the leak, the Coordinator should weakly capture the coordinator instance. The revised implementation of the FixedCallbackButtonView struct features a makeUIView function that weakly captures the context.coordinator, thereby preventing strong retention and the subsequent memory leak. Additionally, the dismantleUIView function is included to clean up the button's onTap reference when the view is no longer needed.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.