Fixed on Xcode 11, Beta 5
There is a problem with
UIViewControllerRepresentable
, where the UIViewControllers
they create are never released. In the code below, I have created a minimal example of the problem. By tapping repeatedly on a button, a UIViewController
gets created. However, when destroyed, the deinit
method is never called. Not even after a simulated Memory Warning.
Bug Report: FB6190972
Workaround: Unknown
Last Version With Bug: Xcode 11, Beta 4.
Stackoverflow Question: https://stackoverflow.com/questions/56699009/swiftui-memory-leak-when-using-uiviewcontrollerrepresentable
Sample Code
This is the code to reproduce the problem:
import SwiftUI
struct ContentView : View {
@State private var showRepView = true
var body: some View {
VStack {
Button(action: {
self.showRepView.toggle()
}, label: { Text("Show Square").font(.title) })
if showRepView {
RepViewController().frame(width: 100, height: 100)
}
}
}
}
struct RepViewController: UIViewControllerRepresentable
{
func makeUIViewController(context: Context) -> SomeCustomeUIViewController {
let vc = SomeCustomeUIViewController()
print("\nmakeUIViewController \(vc)")
return vc
}
func updateUIViewController(_ uiViewController: SomeCustomeUIViewController, context: Context) {
print("updateUIViewController \(uiViewController)")
}
static func dismantleUIViewController(_ uiViewController: SomeCustomeUIViewController, coordinator: Self.Coordinator) {
print("dismantleUIViewController \(uiViewController)")
}
}
class SomeCustomeUIViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.green
print("viewDidLoad \(self)")
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
print("viewWillDissapear \(self)")
}
deinit {
print("DEINIT \(self)")
}
}
Debug Output
This is the output of the example. It shows that all phases work fine. Even the dismantleUIViewController
and viewWillDissapear
are called. Unfortunatey deinit
is not. Using Instruments we can confirm that the UIViewControllers sticks around, and never go away 🙁
makeUIViewController <Representable.SomeCustomeUIViewController: 0x7fe90670e520>
viewDidLoad <Representable.SomeCustomeUIViewController: 0x7fe90670e520>
updateUIViewController <Representable.SomeCustomeUIViewController: 0x7fe90670e520>
updateUIViewController <Representable.SomeCustomeUIViewController: 0x7fe90670e520>
dismantleUIViewController <Representable.SomeCustomeUIViewController: 0x7fe90670e520>
viewWillDissapear <Representable.SomeCustomeUIViewController: 0x7fe90670e520>
makeUIViewController <Representable.SomeCustomeUIViewController: 0x7fe906603570>
viewDidLoad <Representable.SomeCustomeUIViewController: 0x7fe906603570>
updateUIViewController <Representable.SomeCustomeUIViewController: 0x7fe906603570>
dismantleUIViewController <Representable.SomeCustomeUIViewController: 0x7fe906603570>
viewWillDissapear <Representable.SomeCustomeUIViewController: 0x7fe906603570>
makeUIViewController <Representable.SomeCustomeUIViewController: 0x7fe90660cea0>
viewDidLoad <Representable.SomeCustomeUIViewController: 0x7fe90660cea0>
updateUIViewController <Representable.SomeCustomeUIViewController: 0x7fe90660cea0>
dismantleUIViewController <Representable.SomeCustomeUIViewController: 0x7fe90660cea0>
viewWillDissapear <Representable.SomeCustomeUIViewController: 0x7fe90660cea0>
Xcode Instruments
And here is a screenshot of Instruments, where we can see that indeed, the UIViewController persists indefinitely.